Confluence Docs 2.10 : Hibernate Sessions and Transaction Management Guidelines
This page last changed on Dec 17, 2007 by jnolen.
Transaction ManagementTransaction demarcation is provided by Spring, with a few wrinkles.
The last point is necessary because in some cases, we were sending redirect responses to the browser then committing the transaction. A quick browser would request the redirect page before their transaction was committed, and view stale data as a result. By committing the transaction before we render the view, we make sure that everything we expect to be in the database is in the database before the browser has a chance to re-request it. Hibernate SessionsSessions are a Hibernate construct used to mediate connections with the database. The session opens a single database connection when it is created, and holds onto it until the session is closed. Every object that is loaded by Hibernate from the database is associated with the session, allowing Hibernate to automatically persist objects that are modified, and allowing Hibernate to implement functionality such as lazy-loading. Disconnected ObjectsIf an object is evicted from its session (for example via a clear, see below), or the session is closed while the object is still kept alive, the object is "disconnected" from the session. A disconnected object will continue to work so long as you don't perform any operation that it needs to go back to the database for, such as accessing a lazily-loaded collection. If you see a LazyInitializationException, it means that a Hibernate-managed object has lived longer than its session. Managed objects are not portable between sessions. Trying to load an object in one session then save it into another session will also result in an error. (You can use Session.load() or Session.get() to re-introduce an object to a new session, but you're much better off fixing whatever problem is causing you to try to move objects between sessions in the first place. CachingStoring hibernate objects in caches is a bad idea. By definition, a hibernate-managed object placed in a cache will outlive its session. Even if caching such an object is safe now, it's quite likely that in the future we might switch some of its properties to be lazily-loaded, or change code-paths so that properties that were previously being loaded before the object was cached aren't being loaded any more. The LazyInitializationException errors that result rarely show up in tests, and are hard to diagnose and fix. Hibernate maintains its own second-level cache (shared between Confluence nodes via Tangosol Coherence) that does not suffer from this problem. Use it in preference to manually caching Hibernate data. If you need to cache information from Hibernate, don't cache the Hibernate objects themselves. A useful alternative is to cache the object's ID and class, and then retrieve the object in the context of the current session using Session.get(class, id). ID lookups go straight through Hibernate's own second-level cache, so are (hopefully) efficient. The getHandle() and findByHandle() methods of the AnyTypeObjectDao provide a helpful API for doing just this. Flushing and ClearingWhen the session persists its changes to the database, this is called "flushing". During a flush, each object associated with the session is checked to see if it has changed state. Any object with changed state will be persisted to the database, regardless of whether the changed objects are explicitly saved or not. You can configure Hibernate's flush behaviour, but the default (FlushMode.AUTO) will flush the session:
How long a flush takes is a function of the number of objects associated with the session. Thus, the more objects you load during the lifetime of a session, the less efficient each query will be (as a flush will generally be done prior to each query). If you have some long-running operation that gets slower and slower and slower as it runs, it's possible that the Hibernate session is the cause. Operations that cycle through large numbers of objects may want to periodically clear() their session. Clearing will dissociate all objects from the session, so they won't build up making flushes slower and slower. Clearing a session does not flush it first, so you'll want to flush manually or you will lose any pending, unsaved changes. Multi-threadingHibernate sessions are not thread-safe. Not only does this mean you shouldn't pass a Hibernate session into a new thread, it also means that because objects you load from a session can be called from (and call back to) their owning session, you must not share Hibernate-managed objects between threads. Once again, try to only pass object IDs, and load the object freshly from the new thread's own session. Spring's transaction management places the Hibernate session in a ThreadLocal variable, accessed via the sessionFactory. All Confluence DAOs use that ThreadLocal. This means that when you create a new thread you no longer have access to the Hibernate session for that thread (a good thing, as above), and you are no longer part of your current transaction. The Session In View FilterConfluence uses the "Session in view" pattern for managing Hibernate sessions. The SessionInViewFilter opens a Hibernate session which is then available for the entire web request. The advantages of this is that you avoid session-related errors:
The disadvantages are:
Non-Web RequestsNon-web requests do not automatically have a Hibernate session to work with, because they don't come in through the Session In View Filter. This includes start-up events, quartz jobs, and any long-running task that spawns a new thread. As a result, a new session will be opened when you make a call to a transaction-managed Spring object, and closed when that call returns. A very common programming error in this context is to retrieve a collection of objects from a manager, then do something to each object. The moment the call to the manager returns, all objects will be detached from their containing session. If you try to do anything to them after that, you won't get the result you expected. I'm not sure if this sequence diagram helps, but here goes... Consider moving such operations into the manager itself, so the whole operation will be wrapped in the one transaction. Alternatively, if making everything run in separate transactions is what you want, have the job retrieve a collection of IDs, and pass those back to the manager one by one. |
![]() |
Document generated by Confluence on Dec 03, 2008 15:13 |