New to Hibernate 3.0.1 is the SessionFactory.getCurrentSession() method. It allows application developers to delegate tracking of current sessions to Hibernate itself. This is fairly trivial functionality, but stuff just about any user of Hibernate had to implement themselves, or rely on third party stuff to do for them. Let's take a look at how this is implemented in Hibernate and how it might be useful.
Context Scope
I said that SessionFactory.getCurrentSession() tracks the current session on behalf of the application developer.
What exactly does that mean? What is the scope
in which a session is considered current
? The transaction!
More specifically, a JTA transaction.
Another dimension to scoping the current session is to which factory it belongs. Because Hibernate implements this internal to the SessionFactory, the current sessions are inherently tracked by that given factory. Internally, the SessionFactory maintains a Map of sessions keyed by JTA transaction. There is little overhead in this since the Map is built lazily, and only utilized during getCurrentSession() calls: if you don't use this feature, the map is never even built.
Example Usage
Imagine a simple scenario coordinating efforts between three DAOs:
public Bid myExposedCmtEjbServiceMethod(Long itemId, Double amount) {
ItemDAO itemDAO = new ItemDAO( getSessionFactory() );
BidDAO bidDAO = new BidDAO( getSessionFactory() );
UserDAO userDAO = new UserDAO( getSessionFactory() );
Item item = itemDAO.load( itemId );
User bidder = userDAO.load( getCurrentUsername() );
return bidDAO.create( item, amount, user );
}
How should each of the DAOs utilize the same session to perform their work? The typical pattern is to use ThreadLocals or similiar contextual storage (perhaps a JBoss TransactionLocal) to maintain the current session within that context. Furthermore, how do we know when this current session should be cleaned up?
The usual pattern to implement these functionalities is that a top-level
service/method
is defined as the service controller which is responsible for opening a session at the start, binding
it to the contextual storage (so other collaborators can find it), and cleaning up
the session at the end of the service processing. A slight twist on this is to use method
interception to apply those behaviours (or aspects) on top of the service controller
method.
Either way, this can be a lot of work to setup requiring that we either:
- modify all the service controller points to perform the open-bind-cleanup functionality
- wrapping all our services (sometimes spuriously) in proxies so that we can intercept the method execution and apply those behavioural aspects
So instead, lets look at using the SessionFactory.getCurrentSession() approach:
public class ItemDAO {
private SessionFactory sf;
public ItemDAO(SessionFactory sf) { this.sf = sf; }
public Item load(Long itemId) {
return ( Item ) sf.getCurrentSession().load( Item.class, itemId );
}
...
}
Here, each of the DAO collaborators simply use the getCurrentSession() method; the things collaborating
with the DAOs do not need to perform anything extra
and we do not need to generate proxies
and method interceptors just to apply the notion of contextual sessions.
So now, by using getCurrentSession() we can easily scope the notion of a current session to the JTA transaction and reuse the same session throughout that JTA transaction. But how do we clean up the session? And how do we manage flushing of the session state with the database?
Auto flush and close
Two new configuration options introduced in Hibernate3 are extremely powerful, especially when combined with the SessionFactory.getCurrentSession(). Both of these are available in the JTA environments, as well as scenarios where application is utilizing the Hibernate transaction-abstraction API.
The first is flush_before_completion, which forces a flush of the session just prior to transaction completion (think Synchronization.beforeCompletion()...). With this setting enabled, we do not have to worry about flushing the session after we are done in order to synchronize in-memory state with the database; Hibernate does it for us (just prior the transaction commit).
The second is auto_close_session, which forces the session to be closed after transaction
completion. In JTA environments, this setting has an additional effect; it forces Hibernate
to release JDBC connections much more aggresively. Basically, Hibernate will obtain a connection,
use it, and then immediately release it back to the datasource. This allows better integration into
JTA environments which implement some form of connection containment
check (i.e. the JBoss CachedConnectionManager).
Conclusion
All of these together allow application developers to free themselves from managing session lifecycle
and have Hibernate do it for them.
Thanks!
Eli
One thing that we needed in our system was the concept of nested sessions.
For example; a thread processing a request opens a session at the service entry point. This session is for the business transaction, during which many things might happen. One of those things might be an operation that needs to be persisted right away (an application level lock, for example, that can't wait until the overall business transaction completes).
Because of this we have implemented the notion of a stack of sessions that could get created throughout the lifetime of the business transaction. This stack doesn't usually get to be more than two deep. Application code always calls getCurrentSession(), getting the correct session depending on the state of the stack.
There are obviously other ways to do this, but that is the way the system has grown up over the past four years.
Do you guys have anything similar in mind?
Also does SessionFactory.getCurrentSession() create a new Session if one is not tied to the thread? That is one thing we do in our app framework.
But, if I have an environment with XA transactions, would you still recommend the useage of getCurrentSession() ?
Absolutely.
Now if the JTA transaction is also an XA transaction, then there is potentially an overhead required for the 2PC, but JTA transactions used in the majority of applications are not (or need not be) XA transactions.
So how do you suspend your transactions and/or obtain a different physical connection from the datasource?
Do you guys have anything similar in mind?
Nope
interesting stuff, but I wonder how dows this relate to non-JTA environments? Can you explain this, too?
Thanks, Olli
Is there a getCurrentTransaction as well?
Nope, kinda silly since it curently is only supported for JTA transactions :)
Perhaps if/when we extend this to the Hibernate Transaction API...
I may be in the wrong place, but i'm trying to build a small prototype showing a 2 phase commit. I'm having trouble implementing this using the hibernate(3) api. How can i make 1 transaction that takes 2 sessions (1 for each DB) where the commit does a commit on both DB's and does a rollback of the first when the second goes wrong. Using the JTA api it works, but using hibernate , i don't see any possibility.
Chris.
But, if I have an environment with XA transactions, would you still recommend the useage of getCurrentSession() ?
Thanks, Bernhard
Is it possible to create a mailing list for Hibernate?
Bernhard
We also tried to search for the getCurrentSession() method in the CVS source but did not find it.
We plan on adding support additionally for applications using the Hibernate Transaction API. That is currently targetted for 3.0.2 or 3.1.
In non-JTA envs where the app is not using the Hibernate Transaction API, there is not much we can do.
Is there a getCurrentTransaction as well?
I want this because I want control over my commit. Perhaps to catch exceptions that happen commit-time, perhaps to lessen the load per transaction. Here I mean that when doing a lot of updates, I found it a lot faster to commit, close and start a session and than build a new transaction, than to do it all in one transaction.
Any thoughts on this?
Groeten,
Friso
Hi,
Iam having a requirement where a parent thread opens multiple child threads.Each child thread makes use of hybernate session(iam using getCurrentSession).If any thing is wrong with any of the threads all the processing of other threads should get rollback.Iam using Transaction Manager to control the trasactions.
But it is said that hibernate session object can't be used by multiple concurrent threads.But i want this functionality.Is there any way i can achieve this?
Hi,
Iam having a requirement where a parent thread opens multiple child threads.Each child thread makes use of hybernate session(iam using getCurrentSession).If any thing is wrong with any of the threads all the processing of other threads should get rollback.Iam using Transaction Manager to control the trasactions.
But it is said that hibernate session object can't be used by multiple concurrent threads.But i want this functionality.Is there any way i can achieve this?