How to save new entities with generated Guid without requiring a select from NH

400 views
Skip to first unread message

guilemsola

unread,
Mar 19, 2012, 5:26:02 PM3/19/12
to nhusers
Hi all,

I have discovered that with nhibernate profiler with entities created
for the very first time...

In my application I create a new entity that has a list of steps, and
each step a list of values. (I'm using inverses on mappings). Also
entities and steps references to master values already in db. So there
is a kind of mix of old and new objects.

When I do the first Save I do Session.Save(entity) and the whole tree
is saved in database as it should be, but NH profiler warns about
that:

Unable to determine if StepValueEntity with assigned identifier
ede6a5ee-b4bd-4f67-9c64-11ef85b7d6ff is transient or detached;
querying the database. Use explicit Save() or Update() in session to
prevent this.

And effectively prior all the new entities insert there are a lot of
useless selects from NH because entities are not on DB.

This is very inefficient, so what is the correct way to store those
new entities and tell NH that they are new?



FYI this is how I do mapping for identity columns, maybe this doesn't
give a clue to nhibernate to know about what are new and already
persisted entities and I should do it in another way.

Id(x =>
x.Id).Column("GUID_PIPELINE_STEP_PARAMETER").GeneratedBy.Assigned();

Thanks in advance

Richard Brown

unread,
Mar 20, 2012, 7:34:14 AM3/20/12
to nhu...@googlegroups.com
If you use assigned-identifier, and mix 'old'+'new' (i.e., persistent+transient) objects, NH cannot determine what has to be inserted and what is already there just by looking at the IDs.

You should load the existing persisted entities first.

var referenceEntity = session.Load<ReferenceType>(idThatIKnowExistsInTheDb);

The best solution is to not use assigned identifiers.



--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.


Ramon Smits

unread,
Mar 20, 2012, 9:28:15 AM3/20/12
to nhu...@googlegroups.com

You can use Session.Save(..) for the newly created objects. This way you force NHibernate to do an INSERT. The problem is that sometimes this fails because a root/parent objects does not yet exists in either the database or set to the objects properties. In that case you should

 * take the lookup for granted as NHibernate does not know if it needs to insert or update.
 * don't use assigned identifiers
 * remove the explicit relation between the child and the parent and don't use treat it like an aggregate

Regarding the 2nd, don't use assigned identifiers, I learned the hard way that assigned identifiers should not be used as primary keys within the database to share as it prevents optimized saving of aggregates in nhibernate. This conflicts with modern principles that you want to generate a key upfront to correlate different actions to. When you need such keys then you should keep the primary key internal/protected so that it does not have assigned identifiers but use for example hilo and have a guid property as a 'natural' key.What I dislike about this is that you will always need to join between the parent/child to fetch the child records. If that key was part of the primary key then you could just do a simple select.

Problem is that in most situations you would want to say to nhibernate 'treat proxies as updates and non proxies as inserts'. That would prevent such lookups.

Hope this helps!

--
Ramon

guilemsola

unread,
Mar 20, 2012, 11:54:38 AM3/20/12
to nhusers
Thank you all for the replies,

in fact most of them scare me a bit because I'm using assigned GUID's
and I see that you discourage this practice... I guess that all
already persisted entities are previously loaded but I clearly
identify from your replies that the tree entities construction in my
application has some flaws.


BTW if I use generated GUID like with comb strategy after every insert
NH will need to query again to retrieve this generated field and at
the end I will need another select for each insert. Right?


Regards,

Guillem Solà

On Mar 20, 2:28 pm, Ramon Smits <ramon.sm...@gmail.com> wrote:
> You can use Session.Save(..) for the newly created objects. This way you
> force NHibernate to do an INSERT. The problem is that sometimes this fails
> because a root/parent objects does not yet exists in either the database or
> set to the objects properties. In that case you should
>
>  * take the lookup for granted as NHibernate does not know if it needs to
> insert or update.
>  * don't use assigned identifiers
>  * remove the explicit relation between the child and the parent and don't
> use treat it like an aggregate
>
> Regarding the 2nd, don't use assigned identifiers, I learned the hard way
> that assigned identifiers should not be used as primary keys within the
> database to share as it prevents optimized saving of aggregates in
> nhibernate. This conflicts with modern principles that you want to generate
> a key upfront to correlate different actions to. When you need such keys
> then you should keep the primary key internal/protected so that it does not
> have assigned identifiers but use for example hilo and have a guid property
> as a 'natural' key.What I dislike about this is that you will always need
> to join between the parent/child to fetch the child records. If that key
> was part of the primary key then you could just do a simple select.
>
> Problem is that in most situations you would want to say to nhibernate
> 'treat proxies as updates and non proxies as inserts'. That would prevent
> such lookups.
>
> Hope this helps!
>
> On Tue, Mar 20, 2012 at 12:34 PM, Richard Brown <fluke...@googlemail.com>wrote:
>
>
>
>
>
>
>
>
>
> > If you use assigned-identifier, and mix 'old'+'new' (i.e.,
> > persistent+transient) objects, NH cannot determine what has to be inserted
> > and what is already there just by looking at the IDs.
>
> > You should load the existing persisted entities first.
>
> > var referenceEntity =
> > session.Load<ReferenceType>(idThatIKnowExistsInTheDb);
>
> > The best solution is to not use assigned identifiers.
>

Ramon Smits

unread,
Mar 20, 2012, 1:27:29 PM3/20/12
to nhu...@googlegroups.com
in fact most of them scare me a bit because I'm using assigned GUID's
and I see that you discourage this practice... I guess that all
already persisted entities are previously loaded but I clearly
identify from your replies that the tree entities construction in my
application has some flaws.

I don't discourage it but it helps preventing this specific issue. If this really creates a performance issue in your application then redesigning this type of aggregates can possibly improve performance. The keys that are communicates out side of such a service don't change.
 
BTW if I use generated GUID like with comb strategy after every insert
NH will need to query again to retrieve this generated field and at
the end I will need another select for each insert. Right?

The comb strategy is when you don't use assigned keys. Such a strategy should not create roundtrips for key validation.

Maybe I do not understand your question.


Ramon

guilemsola

unread,
Mar 20, 2012, 1:18:14 PM3/20/12
to nhusers
Ok, I see it.

If I use a generator like guid comb NH doesn't need to do a select to
know which ID has been assigned and it easily knows what is a new and
an old object.

Thank you all for your help!

Guillem

Anne Epstein

unread,
Mar 20, 2012, 3:08:49 PM3/20/12
to nhu...@googlegroups.com
Guillem,
You're going to have an easier time if you go with some of the other
responses you've already received, but if you don't want to change
your Id column yet adding a column is an option for you, an
alternative you can try is to use a version column. Doing so should
allow you to use an assigned id but avoid these sort of assigned-id
problems you've been facing, such as having to explicitly choose save
or update. Since I think you're using Fluent NH, that might look
like:

Version(entity => entity.Version)

I believe you could alternatively go with timestamp here, but haven't
done that personally.

Hope that helps!

guilemsola

unread,
Mar 20, 2012, 5:42:05 PM3/20/12
to nhusers
Thanks Anne,

I had already considered the version option but I discarded it as I
considered too much to add an extra column in every table.

Finally I decided to use a guid comb generated that took me 10 minutes
with fluent to change on mappings and now there are no more select
prior inserts.

Regards

Guillem Solà

costa

unread,
Nov 23, 2012, 2:27:04 PM11/23/12
to nhu...@googlegroups.com
Hello:

I have the same problem. I have two tables TParent/TChild where TChild is a dependent table, i.e.

TParent:
id (PK) - identity field (I am using Sql Server 2008)
name

TChild
ParentId (PK1) -> TParent.Id
Index  (PK2)
...

The combination ParentId/Index is unique.



When I save the entities:
I have something like this:

var parent = new TParent();
var child = new TChild();
child.Index = 1;
child.Parent = parent;

parent.Children.Add(child);

child = new TChild();
child.Index = 2;
child.Parent = parent;

parent.Children.Add(child);

Session.SaveOrUpdate(parent);

When this runs, I get the warning:

 Unable to determine if ... with assigned identifier is transient or detached

I run a sql trace and before NH executes the child inserts it executes select statements to see if the children are there.

I am still using NH 3.1.0. I will upgrade to 3.3.1 soon, but I was curious why NH can't figure out that the children are new.


Calling Session.Save(child) before saving the parent (and right after the child was added to the Children collection) doesn't seem to work because it fired the child inserts before the parent was created


Thanks

Pete Appleton

unread,
Nov 26, 2012, 4:44:23 AM11/26/12
to nhu...@googlegroups.com

I suspect that you've not told NHibernate how to know that it's a new child (by way of the unsaved-value attribute.  However, is it really true that the index is part of the child's PK as opposed to a UQ?  My reading of the pseudo-code is that you've got a parent with an ordered list of children; is the order guaranteed immutable (including no deletes, ever!) and truly an identifier?  If you ever delete or re-order children then I'd suggest that the child is promoted to a 'first class' entity with it's own ID.  FWIW, when I have this situation my pattern tends to be:

 

TChild:

Parent {get;protected set;}

Index { get { return Parent.Children.IndexOf(this); } }

Child(Parent parent) {

if(parent == null) throw;

}

 

/Pete

--

You received this message because you are subscribed to the Google Groups "nhusers" group.

To view this discussion on the web visit https://groups.google.com/d/msg/nhusers/-/ygu-KT2PgY4J.

costa

unread,
Nov 26, 2012, 3:55:42 PM11/26/12
to nhu...@googlegroups.com
@PeteA.

Yes, the index is part of the primary key. the database terms, Child is what is called in database terms a dependent entity. Yes, children could be deleted, but the only assumption you can make is that, for a given parent, the Index is unique among children. In this particular case the order doesn't matter.

I guess in my scenario, NH doesn't know whether the child object it's a new one or not, and that's acceptable, even though logically it could mark the children as new because their parent is new (which NH should not have a problem detecting).

From a database modeling point of view,  it doesn't always make sense to assign surrogate primary keys to dependent entities. The example that I gave was simplified. In the real world the children of the parent are description records in different languages of the parent. Instead of the Index field I have a language code/country code, so the child key is made of parentId/language code/country code. The language code and the country code are Foreign Keys to a table that contains Cultures.

I still think that NH can mark the object as new if one of the FK objects that is also PK field is new.

Thanks

Pete Appleton

unread,
Nov 27, 2012, 6:49:55 AM11/27/12
to nhu...@googlegroups.com

Yes, I'm quite familiar with dependent entities... unfortunately, the simplified code in your original question doesn't seem to match your current question and so of course the answer is different too!  For the question you're now asking, I'd suggest looking at a component-id (http://nhforge.org/doc/nh/en/index.html#components-compositeid) plus a version to allow transience detection.

To view this discussion on the web visit https://groups.google.com/d/msg/nhusers/-/gA9HbYDZSDwJ.

costa

unread,
Nov 27, 2012, 6:28:39 PM11/27/12
to nhu...@googlegroups.com
@PeteA: Thank you for the pointer to the documentation. I think I am going to implement the IInterceptor.IsTransient() method. I really liked the version column idea, it's probably the simplest, but at this stage it is too late to add these columns.

Reply all
Reply to author
Forward
0 new messages