-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 22 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Temporal Primary Key
PostPosted: Tue May 09, 2006 8:06 pm 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Hi!
While reading in the forums, I found that:

Code:
session.save(newObject)


always saves to the database if the "id" is "native" (in SQLServer case, if it is AutoIncrement)

So... i have been wondering... couldnt NHibernate asign a "temporal" primary key to the object until the transaction is commited or the session is flushed? has someone else tried this?


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 10:12 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Temporal means relating to time or the physical world.. sort of the opposite to spiritual. So I guess that's not what you meant. Can you explain your idea in a different way? What is it that you'd like to see happen to a transient object with an id generator="native" when it's saved?

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 10:52 pm 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Hi!
By "temporal" I mean "only until the transaction is commited"

something like:

Code:
SomeClass newObject = new SomeClass();// at this moment if the pk  is null
trsct=session.BeginTransaction()
session.save(newObject) // at this moment if the pk is -1;
trsct.Commit();
Assert.IsTrue(newObject.id == 36) // at this moment if the pk is 36;


or like:

Code:
SomeClass newObject = new SomeClass();// at this moment if the pk  is null
session.save(newObject) // at this moment if the pk is -1;
session.Flush();
Assert.IsTrue(newObject.id == 36) // at this moment if the pk is 36;


I would like that to be the behavior of PKs for FlushMode.Never and FlushMode.Commit for native ids.

Basically what I want is an hybrid between "increment" and "native" so that before the transaction is commited, the object gets an "increment" negative id, and after commit the object get a "real" id. (this is the way it works with the d... DataSet, and it is one of the few interesting things it does... it is also the way Apple's EOF ORM( http://en.wikipedia.org/wiki/Enterprise ... _Framework ) works


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 11:03 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
I'm not sure that you'd need anything like that. Isn't the id poopulated as soon as you call save? Do you really have to commit the transaction?

Even if the transaction does need to be committed, do you actually need to know the id at that point? Other objects that need to know the id should be rewritten to contain the object, rather than the id (use an association like many-to-one). If you want to find out if the object has been saved but not committed, you can check the id against whatever you've set the unsaved-value to: if the id is the unsaved value, and session.contains(object) returns true, then the object has been saved but not committed (this assumes that the id isn't populated simply by saving the object).

P.S. You could use "temporary" instead of temporal :)

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 11:28 pm 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
tenwit wrote:
I'm not sure that you'd need anything like that. Isn't the id poopulated as soon as you call save? Do you really have to commit the transaction?


Basically what I want, is not having to "call the database" to have an object in session cache, if I was going to cancel the transaction, why should NHibernate go fetch the id to the database?

Imagine, for example, an ASP.NET application, and you want to have your object in nhibernate cache so that you can fetch if using "session.Get" between requests, but you don't want to save it until the "end" button in a 10 steps wizard is reached... how do you typically solve that problem?

I mean, if I set FlushMode.Never... it should really be never (not, never, but only if you are not using native ids)... and if I set FlushMode.Commit it should really don't talk to the database unless I really really want to commit... also I think temporary primary keys would be more scalable, because I wouldn't need to talk to the database until the user is sure he really wants to save... dont you think?


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 11:37 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
First thing to do is to find out for sure which way it works. After all, if it already works the way you want it to, then no harm, no foul.

If it doesn't work the way you want it to, then wouldn't it be more appropriate to not save the object until you know you want to commit the transaction? Why tell hibernate that the object even exists? I agree that getting the id from the DB and then not using the id is a waste, but forcing hiberante to put a fake id into the object to tell the API user that the object has been passed to save() but hasn't been flushed is a bad idea. It's what my old university lecturer called "logical programming", and it was old and disapproved-of back in the '80s, when I was in university.

BTW, with SQLServer's identity column type, there is no database access to get the id until flush is called. So using save(obj) is ok, and it does what you want. I don't know about the various other generators that are covered by generator class="native", so it's probably not a good idea to depend on that behaviour.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 10, 2006 12:17 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
tenwit wrote:
First thing to do is to find out for sure which way it works. After all, if it already works the way you want it to, then no harm, no foul.

I am not sure what do you mean with that... but I am debugging my application, and seeing the the SQL sent to the SQLServer database using the SQLServer Profiler... and there is a database access to get the id before flush is called


tenwit wrote:
If it doesn't work the way you want it to, then wouldn't it be more appropriate to not save the object until you know you want to commit the transaction?

Perhaps... but then tell me... what would you say is the recommended way to have multiple-web-requests but only one database transaction? having my own cache? (wouldnt it be nice if nhibernate could provide me with that service even if I am using native ids?)

Quote:
It's what my old university lecturer called "logical programming", and it was old and disapproved-of back in the '80s, when I was in university.

So.. he liked ilogical programming ;) I really hope that was a joke...

tenwit wrote:
BTW, with SQLServer's identity column type, there is no database access to get the id until flush is called.

I am sad to tell you this, but I am testing it right now with the SQLProfiler2005, and there is a database access to get the id before flush is called. (I am working with NHibernate 1.02)


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 10, 2006 1:13 am 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
luxspes wrote:
what would you say is the recommended way to have multiple-web-requests but only one database transaction?

Multiple web requests but only one transaction? You mean that you're creating an object in a transaction, saving it, not committing, and then forwarding to a new web page? And waiting for more user input before committing the transaction? That's a really bad idea, I presume that you're not doing that. If you need to visit two or more web pages in order to create and save an object, then I'd imagine that the correct thing to do is to pass all the values from the first page to the second (and subsequent) pages (as hidden fields), then create and save the object(s) all in one go, at the end.

luxspes wrote:
So.. he liked ilogical programming ;) I really hope that was a joke...

Heh. Not quite.. logical programming (as he defined it) means using a parameter or property to change how a method or action works. For example, putting a special value in an ID, a value that isn't an ID but means "ID will be generated", which you would then use to do different things at a later point. Post-punchchard programming standards usually require that "getID()" returns an ID, and nothing else. It's exactly the same thing as not returning result codes from methods: either a method works or it throws an exception, you shouldn't return false on failure.
</rant>

luxspes wrote:
I am sad to tell you this, but I am testing it right now with the SQLProfiler2005, and there is a database access to get the id before flush is called.

I hope that you'll find that there is another call to flush made, presumably deep in some hibernate code, that's causing that. If there isn't, then I think that there is a real problem there: I could easily write code that would cause that ID-reading select statement to lock up. But either way, you definitely need to take some steps to avoid that access until the end of the transaction. Not calling save on the new object (at that point) is probably a reasonable solution.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 10, 2006 11:06 am 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Hi!
(First tenwit, I'd like to thank you for taking the time to answer all my questions... and here are some more ;) )

Quote:
You mean that you're creating an object in a transaction, saving it, not committing, and then forwarding to a new web page? And waiting for more user input before committing the transaction? That's a really bad idea, I presume that you're not doing that. If you need to visit two or more web pages in order to create and save an object, then I'd imagine that the correct thing to do is to pass all the values from the first page to the second (and subsequent) pages (as hidden fields), then create and save the object(s) all in one go, at the end.


That is the way I used to do it with Enteprise Object Framework (from NextStep now Apple), and I have done it with datasets... why should I "repeat my self" by having two objects (one to pass between web-requests, and one to finally save) I dont see any advantages in doing that (but it seems to be common practice in Hibernate world... so I do it the way you say it... but that doesnt mean I like it, it is a lot of extra work (specially with a complex UI) and i can not see the advantage.
Imagine for example, the typical shopping cart application, why should I need to save the products in the cart into the database if the user hasn't checked out (of course, a requirement of the system could be to remember those, but what if the customer requirment is to forget them?)

Quote:
Heh. Not quite.. logical programming (as he defined it) means using a parameter or property to change how a method or action works. For example, putting a special value in an ID, a value that isn't an ID but means "ID will be generated" ... either a method works or it throws an exception, you shouldn't return false on failure.


On this, I completly agree with you, if a method gets the wrong input parameters, the answer should be exception, you shouldn't return false on failure. But... in webapps, the UI is unable to tell the difference between one object an the other without an ID (we only use IDs because the database needs them, a pure object model certainly doesn't need them) but, if our web based presentation layer needs them? I can't select an unsaved object in the gridview if it doesn't have some kind of id... shouldn't it be easier? why the database gets the benefit of ids but the UI doesn't?

When you build your User Interfaces... do you really follow the principle of "provide a safe environment: the data is saved if an only if the "save" button is clicked, and if it doesnt, or the cancel button is clicked everything is rollbacked?" (providing a safe environment for the user to experiment makes it easier for him to learn how to use it... and it shouldn't be that hard to build.. don't you think? ( http://luxspes.blogspot.com/2006/05/ui- ... -save.html )

Quote:
I hope that you'll find that there is another call to flush made, presumably deep in some hibernate code, that's causing that. If there isn't, then I think that there is a real problem there


You are right... it sounds ilogical ;)... I am going to take a look at the inner workings of autoincrement based ids, and find out exactly what instruction is triggering the flush, so that I can disscuss it with more information in my hands.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 10, 2006 5:30 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
I think I have a solution for you. Hibernate docs frequently mention a business key, but never mention what you can use them for. It looks to me like that's exactly what you need: a pure business key.

Your objects need a unique identifier, so that they can be referenced by lists and links in your web front end. They also need a database identifier, obviously. And in many cases, where you can create and save an object in one go, then these two keys can be the same.

But for your shopping cart example, I'd recommend having two separate keys. Your hibernate <id> key will be the database key, but you'll also have another field, probably a GUID (so that you can generate it on any machine at any time and be guaranteed that you're not going to violate any uniqueness constraints) that is an object's business key (or at least, a prime component of the business key). This will allow you to uniquely refer to unsaved objects, and avoids having to go to the database to get a unique id.

I think that if you do this, you'll be able to create your objects and pass them around your webpages without having to save them before you really need to.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 12, 2006 1:44 pm 
Newbie

Joined: Tue May 02, 2006 6:54 pm
Posts: 10
Location: Salt Lake City, Utah
Multiple web requests but only one transaction? You mean that you're creating an object in a transaction, saving it, not committing, and then forwarding to a new web page? And waiting for more user input before committing the transaction? That's a really bad idea, I presume that you're not doing that. If you need to visit two or more web pages in order to create and save an object, then I'd imagine that the correct thing to do is to pass all the values from the first page to the second (and subsequent) pages (as hidden fields), then create and save the object(s) all in one go, at the end.

Isn't that what long conversation with a session-per-conversation is about according to hibernate documentation:

http://www.hibernate.org/42.html#A3
http://www.hibernate.org/43.html (HibernateSessionConversationFilter)

I'm trying to implement this pattern but apparently FlushMode.NEVER doesn't work with native ids?

Someone on a forum had a solution for java version of Hibernate. Does anyone have a solution for NHibernate?

Andre


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 12, 2006 2:20 pm 
Expert
Expert

Joined: Tue Aug 23, 2005 5:52 am
Posts: 335
That's a problem for me too. What's the thread on the Hibernate forum with a solution?

Symon.


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 12, 2006 3:21 pm 
Newbie

Joined: Tue May 02, 2006 6:54 pm
Posts: 10
Location: Salt Lake City, Utah
merge_s.rottem wrote:
That's a problem for me too. What's the thread on the Hibernate forum with a solution?

Symon.


Here's a link:

http://forum.hibernate.org/viewtopic.ph ... =flushmode

I haven't verified that it actually works with native IDs and, honestly, I don't know how, if it works,. Using native key generator like IDENTITY requires insert befor obtaining key!

Andre


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 14, 2006 7:01 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
apiwoni wrote:
Isn't that what long conversation with a session-per-conversation is about according to hibernate documentation:

Definitely not. Both of those pages say that long conversations/sessions are defined by not closing the session after the transaction. That is, you do what a request needs to do in a transaction, then commit that transaction. You don't close the session, you keep it around for use with other transactions. No documentation suggests that you keep a transaction open for longer than absolutely necessary.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Sun May 14, 2006 10:17 pm 
Regular
Regular

Joined: Fri Jul 29, 2005 9:46 am
Posts: 101
Quote:
Definitely not. Both of those pages say that long conversations/sessions are defined by not closing the session after the transaction. That is, you do what a request needs to do in a transaction, then commit that transaction. You don't close the session, you keep it around for use with other transactions. No documentation suggests that you keep a transaction open for longer than absolutely necessary.


Exactly, that is why I think there is something fundamentally too complicated with this approach... what happens when you have a complex UI, that dinamically loads more objects as the user advances throught the use case (perhaps a TreeView) and you want everything either to succeed or to fail (you want to give your user a sense of safety, and if he clicks cancel after a long time playing with the UI, everything should rollbacked, but, with all this manual transaction handling... doing that, from the developer point of view... is somewhat difficult) wouldn't it be nice if we could do something like:

Code:

 
  ObjectContext oc = new ObjectContext();
  Customer newCustomer = new Customer();
  oc .WillAdd(newCustomer);
  newCustomer.Name = "Air Software Ltd";
  newCustomer.Telephone = "+44 (0)121 243 6689";

  TelephoneNumber alternateTelephoneNumber = new TelephoneNumber();
  oc.WillAdd(alternateTelephoneNumber);
  alternateTelephoneNumber.Name := "Home number";
  alternateTelephoneNumber.Number = "+44 (0)121 999 9999";
  newCustomer.ContactDetails.Add(alternateTelephoneNumber)

  oc.UpdateDatabase()



And forget (if we like to do so) about having to manually handle sessions and transactions... I am not saying this other API would be more powerful or that it should replace the current way... but I think that for some systems it would provide a lot of simplicity. (And my main doubt is.. why it hasn't been done? I know, I know, it might not be the most efficient way, but efficiency consideration are not the same for windows and for the web, for small and for big applications, and if we admit that sometimes sending a direct SQL instruction to the database improves performance, then we need to admit to that an API for automatic transaction management could be a good idea for some systems)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.