I have implemented a Hibernate solution that I belive should greatly reduce the chances of a concurrent serializable transaction conflict from occurring when using databases with serializable isolation. I would appreciate a review of this solution, by the users of this forum, to make sure I haven't missed something.
A user on the Mckoi forum was having trouble with running the Hibernate 3.0 example, i.e. '>build.bat eg', against Mckoi database. Mckoi has 'transaction serializable' isolation. 'Read commited' isolation is not available.
The exception was:
Code:
[java] 08:20:44,741 ERROR JDBCTransaction:104 - JDBC commit failed
[java] com.mckoi.database.jdbc.MSQLException: Concurrent
Serializable Transaction Conflict (2): Table altered/dropped: AuctionUser
I suspect that there was more than one connection involved, and the Connection that raised the exception was working from a stale 'image' of the database. So, as a temperary solution I had the user set autocommit to true in /etc/hibernate.properties.
Code:
hibernate.connection.autocommit true
This 'fixed' the problem. Of course this isn't much of a solution if multiple DML statements are occurring within what is supposed to be a single Hibernate transaction.
I had an idea of what I wanted, I implemented it, and it appears to work. The idea was to always 'begin' a Hibernate transaction, i.e. session.beginTransaction() with either a commit() or rollback() to the database to ensure that the connection had the freshest possible 'image' of the database before the session did any work.
I checked the code in Hibernate 3.0 and did not find any commit() or rollback() occurring at beginning of transaction.
So, I extended JDBCTransaction, which does the desired rollback() on the overridden begin() method. There is a Factory class also, which needs to be used to get the new Transaction class. The code for properties file and two classes follows.
Change transaction factory in hibernate.properties.
Code:
hibernate.transaction.factory_class
org.hibernate.transaction.SerializableIsolationTransactionFactory
transaction class SerializableIsolationTransaction
Code:
package org.hibernate.transaction;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
import org.hibernate.jdbc.JDBCContext;
public class SerializableIsolationTransaction extends JDBCTransaction {
private static final Log log = LogFactory.getLog(SerializableIsolationTransaction.class);
private final JDBCContext jdbcContext;
public SerializableIsolationTransaction(JDBCContext jdbcContext, TransactionFactory.Context transactionContext) {
super( jdbcContext, transactionContext );
this.jdbcContext = jdbcContext;
}
public void begin() throws HibernateException {
try {
jdbcContext.connection().rollback();
} catch (SQLException e) {
log.error("begin() rollback failed", e);
throw new TransactionException("begin() rollback failed: ", e);
}
super.begin();
}
}
the factory
Code:
package org.hibernate.transaction;
import java.util.Properties;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.Transaction;
import org.hibernate.HibernateException;
import org.hibernate.jdbc.JDBCContext;
public final class SerializableIsolationTransactionFactory implements TransactionFactory {
public ConnectionReleaseMode getDefaultReleaseMode() {
return ConnectionReleaseMode.AFTER_TRANSACTION;
}
public Transaction beginTransaction(JDBCContext jdbcContext, Context transactionContext) throws HibernateException {
JDBCTransaction tx = new SerializableIsolationTransaction( jdbcContext, transactionContext );
tx.begin();
return tx;
}
public void configure(Properties props) throws HibernateException {}
}
Please advise.
Thanks & Regards, Jim