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).

Wednesday, December 17, 2008

Eclipse/Tomcat note to self

Tomcat stops reading config data.  Error with docBase, directory does not exist or is not readable.  Here's what seemed to help:

Remove Tomcat as a server.  Close and restart Eclipse.  Add Tomcat as a server, with the correct project.  Re-add /Catalina/localhost/my.xml.  Make sure the entry is in my.xml and not in server.xml.

Friday, September 26, 2008

Dear Internet,

I swear to god I'm going to buy just so I can put a bigass message up on port 8080 saying REREAD YOUR URL, DUMBASS.

Sincerely yours,

Tuesday, May 20, 2008

I'll forget this if I don't save it

If you use a framework like Struts, you will find that your internal file paths don't match your external ones. So you may have /foo/bar/myThingy.action (externally) which is mapped to /WEB-INF/secure/jsps/myThingy.jsp internally.

If you use SiteMesh, the decorator patterns you put in decorators.xml should be mappings to your internal directory structure, not your external structure.

Friday, March 21, 2008

Hibernate Session vs. EntityManager

The answer to the question below is depressingly easy. I was overthinking the problem. Session is Hibernate's custom implementation; EntityManager is the standard JPA creature. If you want to stick to the standards (in this case, I do, although the odds of our shifting to a different JPA provider are very slender), stick to EntityManager. The big thing I'm missing out on so far is access to Hibernate's various CascadeTypes; the JPA default CascadeTypes are rather limited.

The way I'm working lately

This is actually almost fun.

Figure out some functionality that needs to exist. Within a unit test, write the top-level code for it regardless of whether the supporting methods exist or not.

List people = populationDao.getPeople();

...even if I haven't written populationDao.getPeople() yet.

Eclipse will complain that I have written references to methods that don't exist. Hit control-1 and choose the answer that is "create method 'getPeople' in interface 'populationDao'." Then, of course, PopulationDaoImpl will have errors, because I have a method in populationDao that I haven't implemented. Open up PopulationDaoImpl, control-1, 'add unimplemented methods,' and write the implementation for getPeople().

This way I know that getPeople() has at least some test coverage right from the start.