Monday, December 22, 2008

Hibernate/JPA objects in an HTTP session

Last week I ran into an unexpected problem.  

My application has a User object, which contains various fields.  User also has a JPA-managed One-To-Many relationship with something called Store.  This relationship had been working fine (and in fact through this story never stopped working).

For the first time, last week, I added a JPA-managed relationship between User and an entity class called QuickListEntry.  QuickListEntry in turn has a relationship with Product, which in turn has plenty of other relationships with other entities.

We're using Struts 2, which is only marginally relevant here.

In Action A, we load up the User object from the SecurityContext, do some stuff with him, and then stuff him into the Session for access later.  In Action B, we pull him out of the session and read his data, including his QuickListEntries.  Well what do you know -- I'm getting a Hibernate LazyInitializationException.  This has never happened before.  It never happed with the User's Stores.  What's going on?

Well here's what's going on.  When the User is stuffed into the Session, he is disconnected from the JPA EntityManager.  When he is pulled out of the Session, his extended relationships (User to QuickListEntry to Product) can't be followed, because he is disconnected from the EntityManager.  

So the most correct solution in this case is not to stuff the User object into the Session at all.  Either stuff the userid in there (which is probably harmless) and then use the userid to re-load the User from the DB in every Action's prepare() method; or pull the User from the SecurityContext on every page load (again, I'd put this in the prepare() method of every Action class).

3 comments:

Luigi said...

What about invoking the getter methods for those relationships?
I mean, when the user is attached to the EM try getting the QuickList and then put the User object in the session.
As default your fetchtype is LAZY and to obtain the lazy-object you have to get it during transaction, managed, otherwise you'll get LazyInitializationException.

Let me know what you think!
Cheers,
Luigi

vince said...

Hi Jim,
On StackOverFlow web site, you've post about "Why doesn't my remove() actually remove reference from DB using JPA @Many-to-Many" but you didn't post your code to explain the solution.
Could you post it, please ?

Regards,

Vince

Jim Kiley said...

Hi Vince,

I'm afraid that that was four clients ago, and I no longer have the code in question. I also haven't been doing JPA in quite a while so I'm not sure I could whip it up from memory. Sorry I can't be of more help!