We went to a CommandPattern with Hibernate over a year ago (2 years?)and it has been working really well for us, especially in a non-jta environment. Essentially, we wanted to be able to enforce units of work to be bound to a single session/transaction w/o having to pass the session around between commands.
The basic gist of this is that when a Command that require db access is first executed it will look for the current session and start a transaction if necessary, execute the rest of the work, which may include other commands and then close off the transaction by either committing or rolling it back.
Initially, we had rolled our own ThreadLocal implementation to keep track of the session and then with the use of a counter determined when we were back to our 'root' command to perform the commit/rollback.
As the Hibernate team rolled out the currentSession() implementation from the SessionFactory we watched in anticipation that we could refactor our code to use the built in mechanism rather than maintaining our own.
Now, I am getting slightly different behaviour between a JTA and Non-JTA environment and want to do a sanity check to see if I am missing something. What initially led me down this questioning route was JBoss reporting that it was cleaning up Connections for me.
The following code is executed via a CommandHandler which is either just a Pojo (non-jta) or a StatelessSessionBean with a "Required" Transaction description (jta).
Code:
public final void execute() throws CommandException {
//wrap the database work in a transaction
Session session = null;
try {
session = HibernateConfigurationHelper.getSessionFactory().getCurrentSession();
Transaction tx = session.getTransaction();
if (tx == null || !tx.isActive()) {
session.beginTransaction();
handlingTransaction = true;
}
} catch (Exception e) {
throw new CommandException(e);
}
try {
doWork();
Transaction tx = session.getTransaction();
if (tx != null && tx.isActive() && handlingTransaction) {
tx.commit();
}
} catch (CommandException ce) {
if (handlingTransaction) {
Transaction tx = session.getTransaction();
if (tx != null && tx.isActive()) {
tx.rollback();
}
}
throw ce;
} catch (Exception e) {
if (handlingTransaction) {
Transaction tx = session.getTransaction();
if (tx != null && tx.isActive()) {
tx.rollback();
}
}
throw new CommandException(e);
}
finally {
if (session != null) {
if (handlingTransaction && session.isOpen()) {
System.out.println("############################# Session is open");
}
}
}
}
The doWork() can be anything including calling other commands but the handlingTransaction variable is used to indicate the root command. When the 'work' is done then it handles either committing or rolling back the transaction.
Now, in a non-JTA environment, by the time it gets to the finally block the session is closed. However, in a JTA environment the session is still open by the time it gets to the finally.
The pertinent properties from the 2 different configs are:
Non-JTA
Code:
props.setProperty("hibernate.connection.release_mode", "auto");
props.setProperty("hibernate.current_session_context_class", "thread");
JTA
Code:
props.setProperty("hibernate.connection.release_mode", "auto");
props.setProperty("hibernate.current_session_context_class", "jta");
props.setProperty("hibernate.transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");
props.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
I feel that I am missing something as the fact that the session is still open doesn't seem right to me. I had thought of simply checking to see if it was open and then close it but then thought against that as it might just be sweeping the problem under the carpet.
By the time our code is executing a transaction is already in an Active state so the newTransaction never gets set and prevents the commit from closing and flushing the session (as far as I can tell).
This is from the begin() method in JTATransaction
Code:
try {
newTransaction = ut.getStatus() == Status.STATUS_NO_TRANSACTION;
if (newTransaction) {
ut.begin();
log.debug("Began a new JTA transaction");
}
}
So I assume the container is starting the transaction as soon as the initial call is made and my begin() and commit() isn't really doing anything beneficial.
But my questions are:
-Does our calling of begin and commit throw anything off the rails? Should we be preventing these calls in a JTA environment?
-Should we be doing something with the session manually? I really don't want to handle it in the ejb as the ejb handles everything and not just Hibernate access.
-Is there some other property setting we should be using?
-Am I completely simple and should I go and find a McJob?