RuntimeException from Connection.rollback/setAutoCommit Corrupts SessionFactory (on a per
thread basis)
-------------------------------------------------------------------------------------------------------
Key: HHH-2827
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2827
Project: Hibernate3
Issue Type: Bug
Components: core
Affects Versions: 3.1.3
Environment: 3.1.3, WebLogic on Windows, DB2 JDBC Driver, DB2 UDB on Windows
Reporter: Mihai Danila
Attachments: HibernateClosedSessionBug.zip
When java.sql.Connection's rollback/setAutoCommit methods throw a RuntimeException, as
is the case in some circumstances in WebLogic, the SessionFactory fails to correctly clean
up the session.
The problem is in class org.hibernate.transaction.JDBCTransaction, lines 161-177. The
block only raises afterTransactionCompletion if (a) the rollback operation within the try
block succeeds or (b) the rollback operation fails with an SQLException. This is wrong, as
reproduced by us in a WebLogic 8.1.3 environment, by causing a database outage at just the
right time.
The net effect is that the session cleanup does not occur, and all subsequent attempts to
use currentSession() on the same transaction factory from the same thread will fail
miserably.
The fact that WebLogic raises a RuntimeException in case of a database outage is a
WebLogic bug that we raised with them. However, according to Sun's document on Javadoc
comments, (
http://java.sun.com/j2se/javadoc/writingdoccomments/#throwstag) Hibernate
should guard against an unchecked exception and still cleanup properly.
"Since there is no way to guarantee that a call has documented all of the unchecked
exceptions that it may throw, the programmer must not depend on the presumption that a
method cannot throw any unchecked exceptions other than those that it is documented to
throw. In other words, you should always assume that a method can throw unchecked
exceptions that are undocumented."
Attached is sample code that shows the problem. I believe the solution is to remove the
fire event call from the try/catch blocks and add it into the finally block, guarding it
with its own finally block, if necessary:
Instead of:
try {
rollbackAndResetAutoCommit();
log.debug("rolled back JDBC Connection");
rolledBack = true;
afterTransactionCompletion(Status.STATUS_ROLLEDBACK);
}
catch (SQLException e) {
log.error("JDBC rollback failed", e);
afterTransactionCompletion(Status.STATUS_UNKNOWN);
throw new TransactionException("JDBC rollback failed", e);
}
finally {
if ( callback ) {
jdbcContext.afterTransactionCompletion( false, this );
}
closeIfRequired();
}
It would be:
try {
rollbackAndResetAutoCommit();
log.debug("rolled back JDBC Connection");
rolledBack = true;
}
catch (SQLException e) {
log.error("JDBC rollback failed", e);
throw new TransactionException("JDBC rollback failed", e);
}
finally {
afterTransactionCompletion(rolledBack ? Status.STATUS_ROLLEDBACK :
Status.STATUS_UNKNOWN);
if ( callback ) {
jdbcContext.afterTransactionCompletion( false, this );
}
closeIfRequired();
}
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://opensource.atlassian.com/projects/hibernate/secure/Administrators....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira