Hibernate doesn’t fetch collections within a bean in the same session the beans were created in. If you need objects in those collections later on within that session, you had better put them in there. This even holds true when you go back to hibernate to refetch the same object instance. Here’s a quick example:
User user = new User("Brian", "Pontarelli"); userDAO.saveOrUpdate(user); Role role = new Role(); role.setUser(user); roleDAO.saveOrUpdate(role); user = userDAO.fetch("Brian", "Pontarelli"); user.getRoles().get(0); // THIS IS NULL!
Once the session is closed out and the fetch is performed in a new session then this will work fine. I can see how this behavior protects the object instance somewhat, but this means that the instance also misses any updates within other threads to its associations. This I haven’t tested because it is a severe pain in the ass, but I’ll probably give it a try tomorrow.
As a quick closing thought, all this ORM stuff in Java seems like such a pain compared to Rails and ActiveRecord. I can understand that transactions can be quite powerful and the POJO model is powerful, but I’m not convinced that this truly makes life that much better as compared to what you gain from a dynamic language’s ability to handle all of the mapping internally and on the fly while making the class look like a simple data holder.
6 thoughts on “Hibernate collection fetching”
Brian, long time no cjug on my part, anyway, I’d agree that Hibernate with XML mapping files and having to hand code DAO object is somewhat tedious compared to rails. I’ve found that adopting a Generics approach to DAO makes things about 50 times easier.
Remember last January (or February) I forget, when Bill Burke presented EJB3 to CJUG at DePaul downtown? He convinced me to take a look at hibernate annotations, and I haven’t looked back since. Also once you start writing generics DAOs things get much simpler.
I’m also pretty certain that the annotation @ManyToOne is going to be a lazy association, that isn’t affected by the problem you had with role and user above. You may have to manually flush the session, but you might not.
you need not create another session, although the cost of that is minimal.
the problem is, they’re using a lazy-load by default, so you’ve got to ensure that your session stays around thru out the duration of the thread that needs access to the database.
consider a more complex model that involved not only users (1..N) roles (N..M) groups, you’d find yourself in a bit of a mess obtaining a fairly complex user structure.
i’m not exactly sure how ruby does this (rails that is) but i can only venture a guess that it’s similiar to the way the lazy-loader in hibernate does it.
bind the session to the thread-local on the request, and that way you can call down as deep as you’d need on a complex model, like the one that you outline or the one that goes one step further, like mine listed above
Actually, what appears to be happening is that since the User is created in the same session that the Role is created, the User’s Set or List of roles is never setup to be a PersistentBag, which is what Hibernate uses for lazy associations.
This actually appears to me to be a bug since I’ve explicitly fetched the User from the session again. What Hibernate should do is NOT return me the instance that I created but instead return me a new instance with the PersistentBag setup correctly. If it needs to copy values from the original User into this new User so that a database round trip won’t occur, so be it. The problem is that the session is caching based on identity and since I constructed the User, it doesn’t contain the correct objects.
And Tim, you had better show up to a few CJUGs soon. I’m going to be stepping down as President since I’m moving back to CO and we definitely need to get together before I split. And also, I definitely agree with the generics thing. We are using Hibernate annotations and generics and without these it would be a total nightmare.
Lazy loading with hibernate or subordinated objects is really a pain in the rear. There has got to be an easier way than worrying about is the session active or not, is the object in the session or not. With all these worries, you might as well hand code JDBC.
This is NOT a bug in Hibernate, come on guys!
You just need to add the role to the collection.
user.getRoles().add( role )
The collection “roles” is a first class persistent object, just like user. If you don’t change the collection, Hibernate won’t know about it. Take the time to read the Hibernate manual on associations again (it takes a while to sink in but it’s worth it!).
FYI, you should probably not be calling saveOrUpdate() all the time: just wrap everything in a transaction. Your code will get a lot cleaner and you will appreciate the value of working entirely in object world (not some SQL leaking mish mash like ActiveRecord).
No one said it was a bug, just an annoyance.
Hence the annoyance. If you create a new session and go fetch the User, the role shows up as long as it is the owning Object (which in my case it is). So, Hibernate sessions behave differently depending on the conditions. This makes them non-idempotent – meaning the same action does not always yield the same results. In programming this is usually considered very BAD.
We’ve all read the docs and I think most people would probably agree that:
1. Hibernates session is not safe and Hibernate tries to do too much work for you when it shouldn’t.
2. Lazy fetching is something distinctly different than what the Hibernate session provides. If the session only provided lazy fetching than my example above would work. In Ruby this is true.
I’ve got to disagree with you on this one. Explicitly telling an Object to save itself is 100% required once you get out of CRUD situations. I might setup an Object and then change it over time and then finally save it. And this might happen across transaction boundaries. I should have complete control over when my Objects are saved and not leave this up to the Hibernate session/transaction, which will never understand completely complex business rules and situations.
Second, as for Ruby’s ActiveRecord, it is very similar to iBatis and other ORM solutions, which are gaining ground because of some of the session issues with Hibernate. Hibernate is also a SQL leaking mish mash and here’s the part that makes it nearly unusable, you don’t know when it will spit out some SQL because the session is a lazy store system meaning it defers inserts and updates until it feels the need to do them. This is VERY VERY dangerous when working in multiple VMs. I mean REALLY REALLY dangerous. Local VMs with a single SessionFactory are fine, but multiple SessionFactories might rely on the database ACID transactions to allow for things like JMS to work properly. When you call saveOrUpdate you should be told immediately if any database constraints have been violated. Hibernate doesn’t do this. It might tell you many lines of code or even methods later. Again, this means that Hibernate is not atomic and in programming this is usually REALLY REALLY BAD.