From: Stefan M. <s....@s2...> - 2004-10-29 17:13:08
|
Hi, I have encountered the following problems with Spring/Hibernate: 1. situation: - there is a thread bound hibernate session. - a method A is called and a transactionInterceptor begins a new Transaction on the thread bound session. - method A calls method B. The invocation is again intercepted by a TransactionInterceptor which behavior is PROPAGATION_REQUIRED. The existing transaction is reused. - An exception is thrown in the body of method B. - The TransactionInterceptor on method B catches the exception and calls sessionHolder.setRollbackOnly(). - The top-level TransactionInterceptor will find that sessionholder.rollbackOnly=true and rollback the transaction. problem: sessionHolder.rollbackOnly is never reset although the transaction is over. Thus the following transactions on this session will be rolled back as well. 2. situation: - a transaction is begun. - different objects are persisted or removed from the database. - the transaction is rolled back. Problem: - The database operations are first put into hibernate's session cache. And they won't be executed/removed until session.flush() or session.clear() are called. The TransactionInterceptor only issues a rollback on the Transaction. Therefore flushing the session after the transaction is over will execute the database operations even though they should have been rolled back. I have fixed these issues by making changes to HibernateTransactionManager, i.e. flushing the session and setting the sessionHolder.rollbackOnly to false before rollback. Changes to HibernateTransactionManager: protected void doRollback(DefaultTransactionStatus status) { HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction(); if (status.isDebug()) { logger.debug("Rolling back Hibernate transaction on session [" + txObject.getSessionHolder().getSession() + "]"); } try { //unset rollbackOnly. txObject.getSessionHolder().unsetRollbackOnly(); try { //flush before rollback to remove all pending batch actions if (flushBeforeRollback) txObject.getSessionHolder().getSession().flush(); } catch (HibernateException e) { logger.error("flushing session [" +txObject.getSessionHolder().getSession() + "] before rollback failed"); } txObject.getSessionHolder().getTransaction().rollback(); } catch (net.sf.hibernate.TransactionException ex) { throw new TransactionSystemException("Could not rollback Hibernate transaction", ex); } catch (JDBCException ex) { // shouldn't really happen, as a rollback doesn't cause a flush throw convertJdbcAccessException(ex.getSQLException()); } catch (HibernateException ex) { // shouldn't really happen, as a rollback doesn't cause a flush throw convertHibernateAccessException(ex); } } Changes to SessionHolder: Added method sunsetRollbackOnly(). Is there another way? -- Stefan Meyer |