-->
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.  [ 9 posts ] 
Author Message
 Post subject: Is reattachment recommended (TransientObjectException)
PostPosted: Thu May 11, 2006 12:53 pm 
Newbie

Joined: Tue Mar 14, 2006 6:48 pm
Posts: 9
Hi:

I am new to Hibernate and was reading a bunch about reattaching detached objects in Hibernate. The reattaching can be done through update or lock which seems to work fine with small object graphs (in my test apps).

What is the experts recommendation for more complex object graphs? Would it be better to pass id's around and refetch the objects into a new session rather than trying to reattach them to that new session? I am getting a org.hibernate.TransientObjectException, but why are my objects transient?

Here is my issue. Object graph:
A User has many Facilities.
A Facility has many Chemicals.
A Chemical has one Chemastr.
A Chemastr has many Chemicals.

A User logs in and I put that User object in the HTTPSession. When the User presses the My Facilities link, I traverse the myFacilities relationship to display the Users assigned Facilities and the # of Chemicals in that Facility.

Sometimes when clicking on the My Facilities link, I get a org.hibernate.TransientObjectException which is caused by the Chemical object. I am reattaching the User object to the new session as follows:

Code:
   public void reattachToSession(Object object) {
      boolean reattachToSession = true;
      if (object instanceof SysIdEntity) {
         reattachToSession = ((SysIdEntity)object).getSyslinkfield() != null;
      }
      if (reattachToSession) {
         log.debug("reattach of "+object.getClass().getName());
         //
         // there seems to be a problem when reattaching from an expired
         // HTTPSession; starting the HMIS application will throw a
         // NonUniqueObjectException; catch it and be done with it
         //
         try {
            //getSessionFactory().getCurrentSession().update(object);
            getSessionFactory().getCurrentSession().lock(object, org.hibernate.LockMode.NONE);
         } catch (org.hibernate.NonUniqueObjectException ex) {
            log.debug("org.hibernate.NonUniqueObjectException: "+ex.getMessage());
         } catch (org.hibernate.TransientObjectException ex) {
            log.debug("org.hibernate.TransientObjectException: "+ex.getMessage());
         }
      }
   }


Here is the output I get:
Code:
DEBUG [FacilityHibernate.reattachToSession 20] reattach of domain.Tbledituser
DEBUG [FacilityHibernate.reattachToSession 38] org.hibernate.TransientObjectException: cannot lock an unsaved transient instance: domain.Tblchemical


If I use update instead of lock (see the commented update method call), then for every transient Chemical it generates a new Id for that Chemical, increasing my sequence number but not using it.
Why are the Chemical objects transient? I don't call explicitly new anywhere in my code, just walk the object graph.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 11, 2006 1:11 pm 
Senior
Senior

Joined: Tue Mar 09, 2004 2:38 pm
Posts: 141
Location: Lowell, MA USA
Objects which are not saved in the database are considered transient. So if the Chemical instance you are locking is not in the database, you will get this exception. Similarly, if you're locking the User instance and it contains a reference to a transient instance, you will also get this exception. Session.lock(object,LockMode.NONE) does not lock on the row in the database. One question though: why do you need to lock the instance?

Ryan-

_________________
Ryan J. McDonough
http://damnhandy.com

Please remember to rate!


Last edited by damnhandy on Wed Jul 05, 2006 9:20 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu May 11, 2006 1:57 pm 
Newbie

Joined: Tue Mar 14, 2006 6:48 pm
Posts: 9
damnhandy wrote:
...Session.lock() puts a lock on the row in the database, it is not an in memory lock. One question though: why do you need to lock the instance?

Ryan-

I have to reattach the User object from the HTTPSession to the Hibernate Session in order to traverse the object graph (getting myFacilities). I have 2 choices: 1. use Session.lock or 2. use Session.update. In both cases I am getting bad behavior.

Case 1. org.hibernate.TransientObjectException
Case 2. The Id field of Chemical is null, therefore generating a new Id, which increments the sequence table (but the Id's are not used)

I don't know where I am getting transient Chemicals. I am not calling new Chemical. As I said above, I am just walking the object graph.

Maybe walking the object graph and reattaching objects to Hibernate Sessions are not recommended?

Michael.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 11, 2006 4:29 pm 
Beginner
Beginner

Joined: Sun Feb 19, 2006 3:50 am
Posts: 34
A null ID field indicates that the object does not represent a row in the database (i.e. it's new and hasn't been saved, and is thus transient). I see two possibilities. One, somewhere in your object graph there is at least one Chemical that is new and hasn't been saved to the database. Or two, somehow something is resetting a Chemical's ID to null. There might be other possibilities, but I don't know what they'd be.

My money's on the first possibility that something's creating a new Chemical and it's part of the object graph, and it's just not obvious where this is happening.


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 13, 2006 10:57 am 
Senior
Senior

Joined: Tue Mar 09, 2004 2:38 pm
Posts: 141
Location: Lowell, MA USA
Quote:
I have to reattach the User object from the HTTPSession to the Hibernate Session in order to traverse the object graph (getting myFacilities). I have 2 choices: 1. use Session.lock or 2. use Session.update. In both cases I am getting bad behavior.


This didn't make sense to me at first, but now it is clear: you're loading a User in one session whithout it's associations, and you're trying reuse the same user instance in another session but you want to load the associated data? However, you are not trying to update the User. Please correct me if I am mistaken.

Reattaching persistent objects to a new session that were loaded in a previous Session is a fully supported feature of Hibernate. However, this feature is generally used for update oprtations, not a "read-more" data operations. I too am grappling with a similar situation where I have a business operation that uses the same data in once transaction and passes it off to another transaction which loads up the same data, plus additional data that would be used for presentaion. Ideally, we'd like to reuse the same instance from transaction 1, but since there is is no good way to achieve this, we have to reload the data.

The two Session methods you have tried will get you into trouble if you are not trying to update the User & Chemicals. I have looked at Session.merge() and Session.load(Object,id) (both of which are vaguely documented in the Java Docs) and merge is the only one which would "kinda" do what you want. However, it does function somewhat like update and you will most lilkely get the issue you have in case2.

One way around this is to utilize the second-level cache and reload the User instance. So you could do something like this:

Code:
/*
* Reloads the user, but will get it from the L2 cache
*/
User user = (User) session.load(User.class,user.getId());
/*
* Load the chemicals from the database if not already in the cache
*/
List chemicals = user.getChemicals();


This will avoid a database hit but achieve the same goal. Another option would be load the user with the data that you will need up front.
But I agree, it could be useful to simply be able to reattach an instance to another session without the intent for an Update.

Ryan-

P.S. don't forget to rate :)

_________________
Ryan J. McDonough
http://damnhandy.com

Please remember to rate!


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 13, 2006 10:58 am 
Senior
Senior

Joined: Tue Mar 09, 2004 2:38 pm
Posts: 141
Location: Lowell, MA USA
Oh yeah, you could also issue a query to just load the associated objects too. Granted they won't be part of the graph, but you'll avoid the relatively minimal database hit of reloading the user instance.

One last thing, I haven't had much suceess in keeping persistent instances in the HTTP Session. In fact, it's so much hassle that I only store the identifier if anything at all. You're also duplicating effort for something that is already available to you in Hibernate. If you want to avoid redundent database calls in between Sessions, use the level 2 cache. That is what the second-level cache is designed to do. Since it's managed by Hibernate and will keep the cached data in sync when the data is modified from within the application. The L2 cache is also quite fast, and if you need a distrubuted cache, JBoss TreeCache and Tangosol Coherence are great options.

Ryan-

_________________
Ryan J. McDonough
http://damnhandy.com

Please remember to rate!


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 16, 2006 1:27 am 
Newbie

Joined: Mon Jan 09, 2006 2:49 am
Posts: 3
One problem with storing persistent objects like you do in the HttpSession is that when it comes time to re-attach that object, it *has* to be done with the same session that opened/fetched it. If you try to attach the object to a different session, you get a NonUniqueObjectException.

This can happen often when you use OpenSessionInView pattern which opens a Hibernate session for a request and closes it at the end of that request. Any persistent objects you retrieved and then try to consequently re-attach to a Hibernate session for a subsequent HttpRequest will throw this exception.


Wayland Chan


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 02, 2006 2:11 pm 
Newbie

Joined: Tue Mar 14, 2006 6:48 pm
Posts: 9
That pretty much answered my questions. Reattaching an object to another session is a hazzle. I have to look into the Level 2 Cache idea (I hope it is transparent and does not involve any coding).

I would prefer to deal with objects rather than with ID's. It's supposed to be Object Oriented Programming. My question for Spring and Struts Web development has and will always be, why do we need to deal with ID's and not objects.

Thanks for all the help.

Michael.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 02, 2006 6:31 pm 
Senior
Senior

Joined: Tue Mar 09, 2004 2:38 pm
Posts: 141
Location: Lowell, MA USA
The L2 cache is very transparent. The only thing you'll need to do is to declare the cache rules in your mapping or config file. Since you're dealing with a collection, you will need to declare that the User to Chemicals association is cachable as well. Just defining a <cache-usage> element on both User and Chemicals is not enough.

Ryan-

_________________
Ryan J. McDonough
http://damnhandy.com

Please remember to rate!


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 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.