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.  [ 4 posts ] 
Author Message
 Post subject: Simultaneous unique object creation in session-per-view pat
PostPosted: Fri Oct 21, 2005 11:38 pm 
Beginner
Beginner

Joined: Tue Jun 07, 2005 11:36 pm
Posts: 22
Hibernate version: 3.0.5 w/ MySQL and Oracle

I am dealing with some implementation issues of the "Session Per View" pattern. Two questions:

1) What is the most elegant way to handle attempts to simultaneously create two objects with the same unique key (or natural-id)? For example, in the CaveatEmptor sample app (caveatemptor.HiA-SE-3.1alpha4), what if two users attempt to create the same Category at the same time? It looks like when the second of the two user sessions goes to commit its transaction, it will get a unique constraint violation. Pretend that this isn't acceptable and we want the app to figure out before hand if it needs to persist a new instance or just update an existing one into the session.

A simple approach is to have a synchronized method in the DAO - Category getOrCreateCategory(String name, Category parent) (where parent is already in the DB). You could even optimize it so that the whole method isn't synchronized, but just the part to do a final check for existence and insert the object. However, from what I can tell from my own experience trying this approach, I believe this means that the transaction must be committed before leaving the synchronized method/block, or else other sessions that are currently open cannot see that the new item has been added and may attempt to add their own instance of it. In other words, each individual object insertion becomes its own unit of work. This totally goes against the "session-per-view" pattern, but I can't figure out what I'm doing wrong. I assume there must be something basic with DB-transactions and lock modes that I am missing.

2) On a related note, say I want to insert a set of Parent objects, each of which has one or more Child objects. Both types use surrogate keys. The Child objects also have a set of properties which form a unique key (natural-id). I have the values which form the natural-id for each of the Child objects, but I don't know which of the Child objects, if any, are already persisted in the DB. I can use a "getOrCreateChild" method like above and do a query for each, persist it if need be, and return the persisted/detached object after committing it, but this seems like "manual" cascading of save/update and I figure there has to be a better way. Is there a standard way for me to just create transient instances of all the Child objects, add them to the Parent's set of children, and then when I save the parent, have Hibernate figure out for me, based on the unique/natural key values, whether it should create a new DB entry for a Child or whether it should simply set the surrogate ID of the child with an existing, matching value?

I realize that I could probably write such a thing myself (with a custom Persistor or Interceptor I think, probably using an Example criteria - haven't researched it that far), but I feel like I'm missing something that must be common knowledge for an easier way to do this.

adTHANKSvance,

Eric


Top
 Profile  
 
 Post subject: Re
PostPosted: Sat Oct 22, 2005 11:17 am 
Newbie

Joined: Wed Sep 17, 2003 3:31 am
Posts: 10
Location: Napoli (IT)
Usually I don't want to see two users inserting the same "Category" successfully. What I really want is the first user performing the insert and the second raising an exception. That's why we have transaction and record locking.

If you really like the second user to overwrite what the first has just created (and so the first will see with surprise something different from what he/she just wrote) you can always implement this logic in the way you described, that is:

- check if the instance exits findind by natural-id
- if yes, update, if no save

There are no reasons for including this in a synchronized block. At least you have to lock (db lock) the instance while finding by natural-id.

In general, synchronized method are seen as a bad practice and Hibernate itself perform no locking at JVM level. By design, all concurrency issues are delegated to the RDBMS.

About the second issue on parent-child: if the child has a unique key consider to model the relationship as a Map making that key as the map-key.
This way Hibernate will do something similar to what you expect when inserting a child that maybe is already in place.

_________________
--- stefano
Don't forget to rate.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 23, 2005 1:02 pm 
Beginner
Beginner

Joined: Tue Jun 07, 2005 11:36 pm
Posts: 22
Sorry, my example was not clear enough. When I say that users may be adding two items with the same natural-id, I mean the items are exactly the same in all ways. Saving one versus the other will not result in either user getting a surprise since it's the same data. It would be more of a surprise if two entries were created (i.e. two Categories with the same name under the same parent would be bad). As another example (one that ties into my second question), imagine a Person - Address relationship where the Address is a separate entity (rather than being a component of the Person as it is in the CaveatEmptor demo). If two new people living at the same address sign on and want to create accounts for themselves, if they click "create" at the same time, you want only 1 new Address entity created.

Regarding the synchronous block, it has been my experience through various tests that you do need it for the reasons I gave in my first post - until the transaction that first creates the address is committed, other sessions/transactions will not find it with searches, so they will attempt to create it, and you thus will get a "duplicate unique key" error when the second transaction commits. I had hoped that enabling the query cache would help this (so that I don't have to commit each Child object in my example individually, but could do a single commit when a save the Parent), but that doesn't appear to make this particular situation easier. I may just be dealing with a fundamental limitation of multiple-writer DB design, but I was hoping that someone would have a more elegant solution than my current "each object is own unit of work" approach that seems to break (or at least seriously warp) the "session-per-view" pattern. At worst, maybe people are just checking for a unique SQL error code that tells them that another transaction committed the same object, and using that to do another check for the object to get it's surrogate Id?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 25, 2005 12:46 pm 
Beginner
Beginner

Joined: Tue Jun 07, 2005 11:36 pm
Posts: 22
For any who are interested...I solved my problems as follows:

I changed the classes in question to use an "assignable" rather than "native" id type. I then set the id to a concatenation of the fields that uniquely identify the object (by having code in the getId method that if id==null, id=field1+"_"+field2). Since the fields are immutable, its a safe way to generate the key.

I did this rather than using a composite key to keep the benefits of having a single, simple object key for use as an object identifier and in foreign key relations. I can now simply create instances of the classes and make them persistent in a given session (usually using a merge to protect against already having a dupe object in my session) without having to lock and check the DB for an unknown surrogate ID. Trade off is that my ids are now Strings rather than Longs, but this isn't that big of deal.

This removes 90% of the situations where I would have to use a synchronized block to search for and possibly add an object when I know fields that uniquely identify it but don't know it's hibernate id. In addition, I set the hibernate.connection.isolation property to TRANSACTION_READ_COMMITTED rather than the default TRANSACTION_REPEATABLE_READ. This makes the remaining case where I still have to do such a thing cleaner because I don't have to open a new session within the synchronized block to ensure that I see any just-committed items. Of course, I could set it all the way down to TRANSACTION_READ_UNCOMMITTED, but even I don't want to play that fast and lose...


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

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.