diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java index 4c2feb32159..73612fe5a3b 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -345,7 +345,7 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan } public boolean isNewConnectionHolder() { - return newConnectionHolder; + return this.newConnectionHolder; } public boolean hasTransaction() { @@ -357,7 +357,7 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan } public boolean isMustRestoreAutoCommit() { - return mustRestoreAutoCommit; + return this.mustRestoreAutoCommit; } public void setRollbackOnly() { diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java index 5b4f5782fd6..57cd94160b0 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,6 +87,10 @@ public abstract class JdbcTransactionObjectSupport implements SavepointManager, return this.savepointAllowed; } + public void flush() { + // no-op + } + //--------------------------------------------------------------------- // Implementation of SavepointManager diff --git a/org.springframework.jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java b/org.springframework.jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java index 703e6930d08..07c6efaf349 100644 --- a/org.springframework.jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java +++ b/org.springframework.jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1958,6 +1958,9 @@ public class DataSourceTransactionManagerTests extends TestCase { public void resume() { } + public void flush() { + } + public void beforeCommit(boolean readOnly) { if (this.status != TransactionSynchronization.STATUS_COMMITTED) { fail("Should never be called"); diff --git a/org.springframework.jms/src/main/java/org/springframework/jms/connection/ConnectionFactoryUtils.java b/org.springframework.jms/src/main/java/org/springframework/jms/connection/ConnectionFactoryUtils.java index c47e407d0c1..a383c3a2fb6 100644 --- a/org.springframework.jms/src/main/java/org/springframework/jms/connection/ConnectionFactoryUtils.java +++ b/org.springframework.jms/src/main/java/org/springframework/jms/connection/ConnectionFactoryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,6 @@ import javax.jms.TopicSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.transaction.support.ResourceHolder; import org.springframework.transaction.support.ResourceHolderSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; @@ -387,7 +386,7 @@ public abstract class ConnectionFactoryUtils { * (e.g. when participating in a JtaTransactionManager transaction). * @see org.springframework.transaction.jta.JtaTransactionManager */ - private static class JmsResourceSynchronization extends ResourceHolderSynchronization { + private static class JmsResourceSynchronization extends ResourceHolderSynchronization { private final boolean transacted; @@ -400,17 +399,17 @@ public abstract class ConnectionFactoryUtils { return !this.transacted; } - protected void processResourceAfterCommit(ResourceHolder resourceHolder) { + protected void processResourceAfterCommit(JmsResourceHolder resourceHolder) { try { - ((JmsResourceHolder) resourceHolder).commitAll(); + resourceHolder.commitAll(); } catch (JMSException ex) { throw new SynchedLocalTransactionFailedException("Local JMS transaction failed to commit", ex); } } - protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { - ((JmsResourceHolder) resourceHolder).closeAll(); + protected void releaseResource(JmsResourceHolder resourceHolder, Object resourceKey) { + resourceHolder.closeAll(); } } diff --git a/org.springframework.jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java b/org.springframework.jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java index a1842610678..e9aad137803 100644 --- a/org.springframework.jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java +++ b/org.springframework.jms/src/main/java/org/springframework/jms/connection/JmsTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -316,6 +316,10 @@ public class JmsTransactionManager extends AbstractPlatformTransactionManager public boolean isRollbackOnly() { return this.resourceHolder.isRollbackOnly(); } + + public void flush() { + // no-op + } } } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTemplate.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTemplate.java index 5025e174059..72962e4087f 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTemplate.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1160,21 +1160,20 @@ public class HibernateTemplate extends HibernateAccessor implements HibernateOpe /** * Check whether write operations are allowed on the given Session. *

Default implementation throws an InvalidDataAccessApiUsageException in - * case of FlushMode.NEVER/MANUAL. Can be overridden in subclasses. + * case of FlushMode.MANUAL. Can be overridden in subclasses. * @param session current Hibernate Session * @throws InvalidDataAccessApiUsageException if write operations are not allowed * @see #setCheckWriteOperations * @see #getFlushMode() * @see #FLUSH_EAGER * @see org.hibernate.Session#getFlushMode() - * @see org.hibernate.FlushMode#NEVER * @see org.hibernate.FlushMode#MANUAL */ protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException { if (isCheckWriteOperations() && getFlushMode() != FLUSH_EAGER && session.getFlushMode().lessThan(FlushMode.COMMIT)) { throw new InvalidDataAccessApiUsageException( - "Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): "+ + "Write operations are not allowed in read-only mode (FlushMode.MANUAL): "+ "Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition."); } } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java index d9005b4101d..2913925c727 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/HibernateTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -828,7 +828,7 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana * Hibernate transaction object, representing a SessionHolder. * Used as transaction object by HibernateTransactionManager. */ - private static class HibernateTransactionObject extends JdbcTransactionObjectSupport { + private class HibernateTransactionObject extends JdbcTransactionObjectSupport { private SessionHolder sessionHolder; @@ -875,16 +875,25 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana } public void setRollbackOnly() { - getSessionHolder().setRollbackOnly(); + this.sessionHolder.setRollbackOnly(); if (hasConnectionHolder()) { getConnectionHolder().setRollbackOnly(); } } public boolean isRollbackOnly() { - return getSessionHolder().isRollbackOnly() || + return this.sessionHolder.isRollbackOnly() || (hasConnectionHolder() && getConnectionHolder().isRollbackOnly()); } + + public void flush() { + try { + this.sessionHolder.getSession().flush(); + } + catch (HibernateException ex) { + throw convertHibernateAccessException(ex); + } + } } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SpringSessionSynchronization.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SpringSessionSynchronization.java index 3b58cd7cf21..aab75ceb202 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SpringSessionSynchronization.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/SpringSessionSynchronization.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -124,6 +124,16 @@ class SpringSessionSynchronization implements TransactionSynchronization, Ordere } } + public void flush() { + try { + SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request"); + getCurrentSession().flush(); + } + catch (HibernateException ex) { + throw translateException(ex); + } + } + public void beforeCommit(boolean readOnly) throws DataAccessException { if (!readOnly) { Session session = getCurrentSession(); @@ -135,17 +145,21 @@ class SpringSessionSynchronization implements TransactionSynchronization, Ordere session.flush(); } catch (HibernateException ex) { - if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) { - JDBCException jdbcEx = (JDBCException) ex; - throw this.jdbcExceptionTranslator.translate( - "Hibernate flushing: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException()); - } - throw SessionFactoryUtils.convertHibernateAccessException(ex); + throw translateException(ex); } } } } + private DataAccessException translateException(HibernateException ex) { + if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) { + JDBCException jdbcEx = (JDBCException) ex; + return this.jdbcExceptionTranslator.translate( + "Hibernate flushing: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException()); + } + return SessionFactoryUtils.convertHibernateAccessException(ex); + } + public void beforeCompletion() { if (this.jtaTransaction != null) { // Typically in case of a suspended JTA transaction: diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java index c910ae09d5e..d5fe4216311 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java @@ -144,10 +144,10 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { * {@link org.hibernate.Session}. Only applied in single session mode. *

Can be populated with the corresponding constant name in XML bean * definitions: e.g. "AUTO". - *

The default is "NEVER". Specify "AUTO" if you intend to use + *

The default is "MANUAL". Specify "AUTO" if you intend to use * this filter without service layer transactions. * @see org.hibernate.Session#setFlushMode - * @see org.hibernate.FlushMode#NEVER + * @see org.hibernate.FlushMode#MANUAL * @see org.hibernate.FlushMode#AUTO */ public void setFlushMode(FlushMode flushMode) { @@ -247,14 +247,14 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { * Note that this just applies in single session mode! *

The default implementation delegates to the * SessionFactoryUtils.getSession method and - * sets the Session's flush mode to "NEVER". + * sets the Session's flush mode to "MANUAL". *

Can be overridden in subclasses for creating a Session with a * custom entity interceptor or JDBC exception translator. * @param sessionFactory the SessionFactory that this filter uses * @return the Session to use * @throws DataAccessResourceFailureException if the Session could not be created * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean) - * @see org.hibernate.FlushMode#NEVER + * @see org.hibernate.FlushMode#MANUAL */ protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java b/org.springframework.orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java index 41f29b2757a..2202cad4891 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -503,7 +503,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager /** * Convert the given JDOException to an appropriate exception from the * org.springframework.dao hierarchy. - *

Default implementation delegates to the JdoDialect. + *

The default implementation delegates to the JdoDialect. * May be overridden in subclasses. * @param ex JDOException that occured * @return the corresponding DataAccessException instance @@ -518,7 +518,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager * JDO transaction object, representing a PersistenceManagerHolder. * Used as transaction object by JdoTransactionManager. */ - private static class JdoTransactionObject extends JdbcTransactionObjectSupport { + private class JdoTransactionObject extends JdbcTransactionObjectSupport { private PersistenceManagerHolder persistenceManagerHolder; @@ -533,11 +533,11 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager } public PersistenceManagerHolder getPersistenceManagerHolder() { - return persistenceManagerHolder; + return this.persistenceManagerHolder; } public boolean isNewPersistenceManagerHolder() { - return newPersistenceManagerHolder; + return this.newPersistenceManagerHolder; } public boolean hasTransaction() { @@ -550,7 +550,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager } public Object getTransactionData() { - return transactionData; + return this.transactionData; } public void setRollbackOnly() { @@ -567,6 +567,15 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction(); return tx.getRollbackOnly(); } + + public void flush() { + try { + this.persistenceManagerHolder.getPersistenceManager().flush(); + } + catch (JDOException ex) { + throw convertJdoAccessException(ex); + } + } } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/jdo/PersistenceManagerFactoryUtils.java b/org.springframework.orm/src/main/java/org/springframework/orm/jdo/PersistenceManagerFactoryUtils.java index d6bafa45155..edf161162f0 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/jdo/PersistenceManagerFactoryUtils.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/jdo/PersistenceManagerFactoryUtils.java @@ -38,7 +38,6 @@ import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; import org.springframework.jdbc.support.SQLExceptionTranslator; import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; -import org.springframework.transaction.support.ResourceHolder; import org.springframework.transaction.support.ResourceHolderSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; @@ -293,7 +292,8 @@ public abstract class PersistenceManagerFactoryUtils { * (e.g. when participating in a JtaTransactionManager transaction). * @see org.springframework.transaction.jta.JtaTransactionManager */ - private static class PersistenceManagerSynchronization extends ResourceHolderSynchronization + private static class PersistenceManagerSynchronization + extends ResourceHolderSynchronization implements Ordered { private final boolean newPersistenceManager; @@ -308,15 +308,24 @@ public abstract class PersistenceManagerFactoryUtils { return PERSISTENCE_MANAGER_SYNCHRONIZATION_ORDER; } + @Override + public void flushResource(PersistenceManagerHolder resourceHolder) { + try { + resourceHolder.getPersistenceManager().flush(); + } + catch (JDOException ex) { + throw convertJdoAccessException(ex); + } + } + @Override protected boolean shouldUnbindAtCompletion() { return this.newPersistenceManager; } @Override - protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { - releasePersistenceManager(((PersistenceManagerHolder) resourceHolder).getPersistenceManager(), - (PersistenceManagerFactory) resourceKey); + protected void releaseResource(PersistenceManagerHolder resourceHolder, PersistenceManagerFactory resourceKey) { + releasePersistenceManager(resourceHolder.getPersistenceManager(), resourceKey); } } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java b/org.springframework.orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java index e5b2f64e144..73ce6020091 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,7 +105,7 @@ public abstract class EntityManagerFactoryUtils { } // No matching persistence unit found - simply take the EntityManagerFactory // with the persistence unit name as bean name (by convention). - return (EntityManagerFactory) beanFactory.getBean(unitName, EntityManagerFactory.class); + return beanFactory.getBean(unitName, EntityManagerFactory.class); } /** @@ -345,41 +345,62 @@ public abstract class EntityManagerFactoryUtils { * (e.g. when participating in a JtaTransactionManager transaction). * @see org.springframework.transaction.jta.JtaTransactionManager */ - private static class EntityManagerSynchronization extends ResourceHolderSynchronization implements Ordered { + private static class EntityManagerSynchronization + extends ResourceHolderSynchronization + implements Ordered { private final Object transactionData; + private final JpaDialect jpaDialect; + private final boolean newEntityManager; public EntityManagerSynchronization( - EntityManagerHolder emHolder, EntityManagerFactory emf, Object transactionData, boolean newEntityManager) { + EntityManagerHolder emHolder, EntityManagerFactory emf, Object txData, boolean newEm) { super(emHolder, emf); - this.transactionData = transactionData; - this.newEntityManager = newEntityManager; + this.transactionData = txData; + this.jpaDialect = (emf instanceof EntityManagerFactoryInfo ? + ((EntityManagerFactoryInfo) emf).getJpaDialect() : null); + this.newEntityManager = newEm; } public int getOrder() { return ENTITY_MANAGER_SYNCHRONIZATION_ORDER; } + @Override + protected void flushResource(EntityManagerHolder resourceHolder) { + try { + resourceHolder.getEntityManager().flush(); + } + catch (RuntimeException ex) { + if (this.jpaDialect != null) { + throw this.jpaDialect.translateExceptionIfPossible(ex); + } + else { + throw convertJpaAccessExceptionIfPossible(ex); + } + } + } + @Override protected boolean shouldUnbindAtCompletion() { return this.newEntityManager; } @Override - protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { - closeEntityManager(((EntityManagerHolder) resourceHolder).getEntityManager()); + protected void releaseResource(EntityManagerHolder resourceHolder, EntityManagerFactory resourceKey) { + closeEntityManager(resourceHolder.getEntityManager()); } @Override - protected void cleanupResource(ResourceHolder resourceHolder, Object resourceKey, boolean committed) { + protected void cleanupResource(EntityManagerHolder resourceHolder, EntityManagerFactory resourceKey, boolean committed) { if (!committed) { // Clear all pending inserts/updates/deletes in the EntityManager. // Necessary for pre-bound EntityManagers, to avoid inconsistent state. - ((EntityManagerHolder) resourceHolder).getEntityManager().clear(); + resourceHolder.getEntityManager().clear(); } - cleanupTransaction(this.transactionData, (EntityManagerFactory) resourceKey); + cleanupTransaction(this.transactionData, resourceKey); } } diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java b/org.springframework.orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java index cb5eb1a08cf..eb5109f9b31 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java @@ -436,7 +436,8 @@ public abstract class ExtendedEntityManagerCreator { * TransactionSynchronization enlisting an extended EntityManager * with a current Spring transaction. */ - private static class ExtendedEntityManagerSynchronization extends ResourceHolderSynchronization + private static class ExtendedEntityManagerSynchronization + extends ResourceHolderSynchronization implements Ordered { private final EntityManager entityManager; @@ -454,6 +455,16 @@ public abstract class ExtendedEntityManagerCreator { return EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER + 1; } + @Override + protected void flushResource(EntityManagerHolder resourceHolder) { + try { + this.entityManager.flush(); + } + catch (RuntimeException ex) { + throw convertException(ex); + } + } + @Override protected boolean shouldReleaseBeforeCompletion() { return false; @@ -467,7 +478,7 @@ public abstract class ExtendedEntityManagerCreator { this.entityManager.getTransaction().commit(); } catch (RuntimeException ex) { - throw convertCompletionException(ex); + throw convertException(ex); } } @@ -480,12 +491,12 @@ public abstract class ExtendedEntityManagerCreator { this.entityManager.getTransaction().rollback(); } catch (RuntimeException ex) { - throw convertCompletionException(ex); + throw convertException(ex); } } } - private RuntimeException convertCompletionException(RuntimeException ex) { + private RuntimeException convertException(RuntimeException ex) { DataAccessException daex = (this.exceptionTranslator != null) ? this.exceptionTranslator.translateExceptionIfPossible(ex) : EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex); diff --git a/org.springframework.orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java b/org.springframework.orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java index b4bc36e791f..4ee8be543de 100644 --- a/org.springframework.orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java +++ b/org.springframework.orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ package org.springframework.orm.jpa; import java.util.HashMap; import java.util.Map; import java.util.Properties; - import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; @@ -553,7 +552,7 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager * JPA transaction object, representing a EntityManagerHolder. * Used as transaction object by JpaTransactionManager. */ - private static class JpaTransactionObject extends JdbcTransactionObjectSupport { + private class JpaTransactionObject extends JdbcTransactionObjectSupport { private EntityManagerHolder entityManagerHolder; @@ -606,6 +605,15 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager return tx.getRollbackOnly(); } + public void flush() { + try { + this.entityManagerHolder.getEntityManager().flush(); + } + catch (RuntimeException ex) { + throw DataAccessUtils.translateIfNecessary(ex, getJpaDialect()); + } + } + @Override public Object createSavepoint() throws TransactionException { return getSavepointManager().createSavepoint(); diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateJtaTransactionTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateJtaTransactionTests.java index ec72010dedb..5d1ae3c0162 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateJtaTransactionTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateJtaTransactionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package org.springframework.orm.hibernate3; import java.util.ArrayList; import java.util.List; - import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; @@ -108,7 +107,7 @@ public class HibernateJtaTransactionTests extends TestCase { session.createQuery("some query string"); sessionControl.setReturnValue(query, 1); if (readOnly) { - session.setFlushMode(FlushMode.NEVER); + session.setFlushMode(FlushMode.MANUAL); sessionControl.setVoidCallable(1); } query.list(); @@ -370,6 +369,14 @@ public class HibernateJtaTransactionTests extends TestCase { } public void testJtaTransactionRollback() throws Exception { + doTestJtaTransactionRollback(false); + } + + public void testJtaTransactionRollbackWithFlush() throws Exception { + doTestJtaTransactionRollback(true); + } + + private void doTestJtaTransactionRollback(final boolean flush) throws Exception { MockControl utControl = MockControl.createControl(UserTransaction.class); UserTransaction ut = (UserTransaction) utControl.getMock(); ut.getStatus(); @@ -390,6 +397,10 @@ public class HibernateJtaTransactionTests extends TestCase { sfControl.setReturnValue(session, 1); session.getSessionFactory(); sessionControl.setReturnValue(sf, 1); + if (flush) { + session.flush(); + sessionControl.setVoidCallable(1); + } sfControl.replay(); sessionControl.replay(); @@ -409,6 +420,9 @@ public class HibernateJtaTransactionTests extends TestCase { return l; } }); + if (flush) { + status.flush(); + } status.setRollbackOnly(); sessionControl.verify(); sessionControl.reset(); @@ -495,7 +509,7 @@ public class HibernateJtaTransactionTests extends TestCase { sessionControl.setReturnValue(true, 5); session.getFlushMode(); if (flushNever) { - sessionControl.setReturnValue(FlushMode.NEVER, 1); + sessionControl.setReturnValue(FlushMode.MANUAL, 1); if (!readOnly) { session.setFlushMode(FlushMode.AUTO); sessionControl.setVoidCallable(1); @@ -546,7 +560,7 @@ public class HibernateJtaTransactionTests extends TestCase { session.flush(); sessionControl.setVoidCallable(1); if (flushNever) { - session.setFlushMode(FlushMode.NEVER); + session.setFlushMode(FlushMode.MANUAL); sessionControl.setVoidCallable(1); } } @@ -1064,15 +1078,15 @@ public class HibernateJtaTransactionTests extends TestCase { session.getSessionFactory(); sessionControl.setReturnValue(sf, 1); session.getFlushMode(); - sessionControl.setReturnValue(FlushMode.NEVER, 1); + sessionControl.setReturnValue(FlushMode.MANUAL, 1); session.setFlushMode(FlushMode.AUTO); sessionControl.setVoidCallable(1); session.flush(); sessionControl.setVoidCallable(1); - session.setFlushMode(FlushMode.NEVER); + session.setFlushMode(FlushMode.MANUAL); sessionControl.setVoidCallable(1); session.getFlushMode(); - sessionControl.setReturnValue(FlushMode.NEVER, 1); + sessionControl.setReturnValue(FlushMode.MANUAL, 1); session.close(); sessionControl.setReturnValue(null, 1); sfControl.replay(); @@ -1306,7 +1320,7 @@ public class HibernateJtaTransactionTests extends TestCase { sfControl.setReturnValue(tm, 7); session.isOpen(); sessionControl.setReturnValue(true, 8); - session.setFlushMode(FlushMode.NEVER); + session.setFlushMode(FlushMode.MANUAL); sessionControl.setVoidCallable(1); session.close(); sessionControl.setReturnValue(null, 2); @@ -1667,7 +1681,7 @@ public class HibernateJtaTransactionTests extends TestCase { sessionControl.setReturnValue(true, 5); session.getFlushMode(); if (flushNever) { - sessionControl.setReturnValue(FlushMode.NEVER, 1); + sessionControl.setReturnValue(FlushMode.MANUAL, 1); session.setFlushMode(FlushMode.AUTO); sessionControl.setVoidCallable(1); } @@ -1702,7 +1716,7 @@ public class HibernateJtaTransactionTests extends TestCase { session.flush(); sessionControl.setVoidCallable(1); if (flushNever) { - session.setFlushMode(FlushMode.NEVER); + session.setFlushMode(FlushMode.MANUAL); sessionControl.setVoidCallable(1); } session.disconnect(); diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateTransactionManagerTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateTransactionManagerTests.java index 6bc7340a729..994995b19ba 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateTransactionManagerTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/HibernateTransactionManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1779,6 +1779,51 @@ public class HibernateTransactionManagerTests extends TestCase { assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); } + public void testTransactionFlush() throws Exception { + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(Session.class); + final Session session = (Session) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.flush(); + sessionControl.setVoidCallable(1); + tx.commit(); + txControl.setVoidCallable(1); + session.close(); + sessionControl.setReturnValue(null, 1); + + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(sf); + tm.setPrepareConnection(false); + TransactionTemplate tt = new TransactionTemplate(tm); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + status.flush(); + } + }); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + protected void tearDown() { assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty()); assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/jdo/JdoTransactionManagerTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/jdo/JdoTransactionManagerTests.java index 4591a7187ec..204d4e1b31d 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/jdo/JdoTransactionManagerTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/jdo/JdoTransactionManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1222,4 +1222,41 @@ public class JdoTransactionManagerTests extends TestCase { queryControl.verify(); } + public void testTransactionFlush() { + pmf.getConnectionFactory(); + pmfControl.setReturnValue(null, 1); + pmf.getPersistenceManager(); + pmfControl.setReturnValue(pm, 1); + pm.currentTransaction(); + pmControl.setReturnValue(tx, 3); + pm.flush(); + pmControl.setVoidCallable(1); + pm.close(); + pmControl.setVoidCallable(1); + tx.begin(); + txControl.setVoidCallable(1); + tx.getRollbackOnly(); + txControl.setReturnValue(false, 1); + tx.commit(); + txControl.setVoidCallable(1); + pmfControl.replay(); + pmControl.replay(); + txControl.replay(); + + PlatformTransactionManager tm = new JdoTransactionManager(pmf); + TransactionTemplate tt = new TransactionTemplate(tm); + assertTrue("Hasn't thread pm", !TransactionSynchronizationManager.hasResource(pmf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread pm", TransactionSynchronizationManager.hasResource(pmf)); + status.flush(); + } + }); + + assertTrue("Hasn't thread pm", !TransactionSynchronizationManager.hasResource(pmf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + } + } diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/jpa/AbstractContainerEntityManagerFactoryIntegrationTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/jpa/AbstractContainerEntityManagerFactoryIntegrationTests.java index a12e9f4af5c..b0644dc1cdc 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/jpa/AbstractContainerEntityManagerFactoryIntegrationTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/jpa/AbstractContainerEntityManagerFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package org.springframework.orm.jpa; import java.lang.reflect.Proxy; import java.util.List; - import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; import javax.persistence.FlushModeType; @@ -95,9 +94,8 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests Person notThere = sharedEntityManager.getReference(Person.class, 666); // We may get here (as with Hibernate). - // Either behaviour is - // valid--throw exception on first access - // or on getReference itself + // Either behaviour is valid: throw exception on first access + // or on getReference itself. notThere.getFirstName(); } @@ -175,8 +173,7 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests } protected void testInstantiateAndSave(EntityManager em) { - assertEquals("Should be no people from previous transactions", - 0, countRowsInTable("person")); + assertEquals("Should be no people from previous transactions", 0, countRowsInTable("person")); Person p = new Person(); p.setFirstName("Tony"); p.setLastName("Blair"); diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/jpa/ApplicationManagedEntityManagerIntegrationTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/jpa/ApplicationManagedEntityManagerIntegrationTests.java index 2ba03fd14f5..d2cf740ab36 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/jpa/ApplicationManagedEntityManagerIntegrationTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/jpa/ApplicationManagedEntityManagerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -93,13 +93,11 @@ public class ApplicationManagedEntityManagerIntegrationTests extends AbstractEnt em.persist(p); em.flush(); - assertEquals("1 row must have been inserted", - 1, countRowsInTable("person")); + assertEquals("1 row must have been inserted", 1, countRowsInTable("person")); } public void testStateClean() { - assertEquals("Should be no people from previous transactions", - 0, countRowsInTable("person")); + assertEquals("Should be no people from previous transactions", 0, countRowsInTable("person")); } public void testReuseInNewTransaction() { diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/jpa/ContainerManagedEntityManagerIntegrationTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/jpa/ContainerManagedEntityManagerIntegrationTests.java index 15845f6453c..22f22831e46 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/jpa/ContainerManagedEntityManagerIntegrationTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/jpa/ContainerManagedEntityManagerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,8 +114,7 @@ public class ContainerManagedEntityManagerIntegrationTests extends AbstractEntit em.persist(p); em.flush(); - assertEquals("1 row must have been inserted", - 1, countRowsInTable("person")); + assertEquals("1 row must have been inserted", 1, countRowsInTable("person")); } public void testReuseInNewTransaction() { diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/jpa/JpaTransactionManagerTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/jpa/JpaTransactionManagerTests.java index 6888d84750f..5a6f76807bf 100644 --- a/org.springframework.orm/src/test/java/org/springframework/orm/jpa/JpaTransactionManagerTests.java +++ b/org.springframework.orm/src/test/java/org/springframework/orm/jpa/JpaTransactionManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1071,4 +1071,33 @@ public class JpaTransactionManagerTests extends TestCase { txControl.verify(); } + public void testTransactionFlush() { + managerControl.expectAndReturn(manager.getTransaction(), tx); + txControl.expectAndReturn(tx.getRollbackOnly(), false); + managerControl.expectAndReturn(manager.getTransaction(), tx); + tx.commit(); + manager.flush(); + + factoryControl.replay(); + managerControl.replay(); + txControl.replay(); + + assertTrue(!TransactionSynchronizationManager.hasResource(factory)); + assertTrue(!TransactionSynchronizationManager.isSynchronizationActive()); + + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue(TransactionSynchronizationManager.hasResource(factory)); + status.flush(); + } + }); + + assertTrue(!TransactionSynchronizationManager.hasResource(factory)); + assertTrue(!TransactionSynchronizationManager.isSynchronizationActive()); + + factoryControl.verify(); + managerControl.verify(); + txControl.verify(); + } + } diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/org.springframework.test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java index 0ca7c294688..596bf5d3e5b 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -335,15 +335,15 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis if (rollbackAnnotation != null) { boolean rollbackOverride = rollbackAnnotation.value(); if (logger.isDebugEnabled()) { - logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback - + "] for test context [" + testContext + "]"); + logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + + rollback + "] for test context [" + testContext + "]"); } rollback = rollbackOverride; } else { if (logger.isDebugEnabled()) { - logger.debug("No method-level @Rollback override: using default rollback [" + rollback - + "] for test context [" + testContext + "]"); + logger.debug("No method-level @Rollback override: using default rollback [" + + rollback + "] for test context [" + testContext + "]"); } } return rollback; @@ -498,6 +498,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis public void endTransaction(boolean rollback) { if (rollback) { + this.transactionStatus.flush(); this.transactionManager.rollback(this.transactionStatus); } else { diff --git a/org.springframework.transaction/src/main/java/org/springframework/jca/cci/connection/ConnectionFactoryUtils.java b/org.springframework.transaction/src/main/java/org/springframework/jca/cci/connection/ConnectionFactoryUtils.java index b3e65604560..3fd28fc9cf0 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/jca/cci/connection/ConnectionFactoryUtils.java +++ b/org.springframework.transaction/src/main/java/org/springframework/jca/cci/connection/ConnectionFactoryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.jca.cci.CannotGetCciConnectionException; -import org.springframework.transaction.support.ResourceHolder; import org.springframework.transaction.support.ResourceHolderSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; @@ -199,15 +198,16 @@ public abstract class ConnectionFactoryUtils { * Callback for resource cleanup at the end of a non-native CCI transaction * (e.g. when participating in a JTA transaction). */ - private static class ConnectionSynchronization extends ResourceHolderSynchronization { + private static class ConnectionSynchronization + extends ResourceHolderSynchronization { public ConnectionSynchronization(ConnectionHolder connectionHolder, ConnectionFactory connectionFactory) { super(connectionHolder, connectionFactory); } @Override - protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { - releaseConnection(((ConnectionHolder) resourceHolder).getConnection(), (ConnectionFactory) resourceKey); + protected void releaseResource(ConnectionHolder resourceHolder, ConnectionFactory resourceKey) { + releaseConnection(resourceHolder.getConnection(), resourceKey); } } diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/TransactionStatus.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/TransactionStatus.java index 7687b1cfa93..54b1f637125 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/TransactionStatus.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/TransactionStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,6 +75,12 @@ public interface TransactionStatus extends SavepointManager { */ boolean isRollbackOnly(); + /** + * Flush the underlying session to the datastore, if applicable: + * for example, all affected Hibernate/JPA sessions. + */ + void flush(); + /** * Return whether this transaction is completed, that is, * whether it has already been committed or rolled back. diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/jta/JtaTransactionObject.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/jta/JtaTransactionObject.java index ea11e006edc..356dd4819bc 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/jta/JtaTransactionObject.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/jta/JtaTransactionObject.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import javax.transaction.UserTransaction; import org.springframework.transaction.TransactionSystemException; import org.springframework.transaction.support.SmartTransactionObject; +import org.springframework.transaction.support.TransactionSynchronizationUtils; /** * JTA transaction object, representing a {@link javax.transaction.UserTransaction}. @@ -72,4 +73,13 @@ public class JtaTransactionObject implements SmartTransactionObject { } } + /** + * This implementation triggers flush callbacks, + * assuming that they will flush all affected ORM sessions. + * @see org.springframework.transaction.support.TransactionSynchronization#flush() + */ + public void flush() { + TransactionSynchronizationUtils.triggerFlush(); + } + } diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/AbstractTransactionStatus.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/AbstractTransactionStatus.java index 7e25c9c4c33..fad8fe35740 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/AbstractTransactionStatus.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/AbstractTransactionStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,6 +89,12 @@ public abstract class AbstractTransactionStatus implements TransactionStatus { return false; } + /** + * This implementations is empty, considering flush as a no-op. + */ + public void flush() { + } + /** * Mark this transaction as completed, that is, committed or rolled back. */ diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/DefaultTransactionStatus.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/DefaultTransactionStatus.java index df0a48bb987..e95879f2670 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/DefaultTransactionStatus.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/DefaultTransactionStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -145,7 +145,7 @@ public class DefaultTransactionStatus extends AbstractTransactionStatus { /** * Determine the rollback-only flag via checking both the transaction object, - * provided that the latter implements the SmartTransactionObject interface. + * provided that the latter implements the {@link SmartTransactionObject} interface. *

Will return "true" if the transaction itself has been marked rollback-only * by the transaction coordinator, for example in case of a timeout. * @see SmartTransactionObject#isRollbackOnly @@ -156,6 +156,16 @@ public class DefaultTransactionStatus extends AbstractTransactionStatus { ((SmartTransactionObject) this.transaction).isRollbackOnly()); } + /** + * Delegate the flushing to the transaction object, + * provided that the latter implements the {@link SmartTransactionObject} interface. + */ + public void flush() { + if (this.transaction instanceof SmartTransactionObject) { + ((SmartTransactionObject) this.transaction).flush(); + } + } + /** * This implementation exposes the SavepointManager interface * of the underlying transaction object, if any. diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/ResourceHolderSynchronization.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/ResourceHolderSynchronization.java index d72fbfd65e7..3becfb0d4cf 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/ResourceHolderSynchronization.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/ResourceHolderSynchronization.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,11 +23,12 @@ package org.springframework.transaction.support; * @author Juergen Hoeller * @since 2.5.5 */ -public class ResourceHolderSynchronization implements TransactionSynchronization { +public abstract class ResourceHolderSynchronization + implements TransactionSynchronization { - private final ResourceHolder resourceHolder; + private final H resourceHolder; - private final Object resourceKey; + private final K resourceKey; private volatile boolean holderActive = true; @@ -38,7 +39,7 @@ public class ResourceHolderSynchronization implements TransactionSynchronization * @param resourceKey the key to bind the ResourceHolder for * @see TransactionSynchronizationManager#bindResource */ - public ResourceHolderSynchronization(ResourceHolder resourceHolder, Object resourceKey) { + public ResourceHolderSynchronization(H resourceHolder, K resourceKey) { this.resourceHolder = resourceHolder; this.resourceKey = resourceKey; } @@ -56,6 +57,10 @@ public class ResourceHolderSynchronization implements TransactionSynchronization } } + public void flush() { + flushResource(this.resourceHolder); + } + public void beforeCommit(boolean readOnly) { } @@ -123,13 +128,20 @@ public class ResourceHolderSynchronization implements TransactionSynchronization return true; } + /** + * Flush callback for the given resource holder. + * @param resourceHolder the resource holder to flush + */ + protected void flushResource(H resourceHolder) { + } + /** * After-commit callback for the given resource holder. * Only called when the resource hasn't been released yet * ({@link #shouldReleaseBeforeCompletion()}). * @param resourceHolder the resource holder to process */ - protected void processResourceAfterCommit(ResourceHolder resourceHolder) { + protected void processResourceAfterCommit(H resourceHolder) { } /** @@ -137,7 +149,7 @@ public class ResourceHolderSynchronization implements TransactionSynchronization * @param resourceHolder the resource holder to process * @param resourceKey the key that the ResourceHolder was bound for */ - protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { + protected void releaseResource(H resourceHolder, K resourceKey) { } /** @@ -147,7 +159,7 @@ public class ResourceHolderSynchronization implements TransactionSynchronization * @param committed whether the transaction has committed (true) * or rolled back (false) */ - protected void cleanupResource(ResourceHolder resourceHolder, Object resourceKey, boolean committed) { + protected void cleanupResource(H resourceHolder, K resourceKey, boolean committed) { } } diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/SimpleTransactionStatus.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/SimpleTransactionStatus.java index f36d6209aeb..0018fc1f475 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/SimpleTransactionStatus.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/SimpleTransactionStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ public class SimpleTransactionStatus extends AbstractTransactionStatus { /** - * Creates a new instance of the {@link SimpleTransactionStatus} class, + * Create a new instance of the {@link SimpleTransactionStatus} class, * indicating a new transaction. */ public SimpleTransactionStatus() { @@ -49,7 +49,7 @@ public class SimpleTransactionStatus extends AbstractTransactionStatus { } /** - * Creates a new instance of the {@link SimpleTransactionStatus} class. + * Create a new instance of the {@link SimpleTransactionStatus} class. * @param newTransaction whether to indicate a new transaction */ public SimpleTransactionStatus(boolean newTransaction) { @@ -58,7 +58,7 @@ public class SimpleTransactionStatus extends AbstractTransactionStatus { public boolean isNewTransaction() { - return newTransaction; + return this.newTransaction; } } diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java index 2c7a00d8fd5..e646c6ecc08 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,4 +39,10 @@ public interface SmartTransactionObject { */ boolean isRollbackOnly(); + /** + * Flush the underlying sessions to the datastore, if applicable: + * for example, all affected Hibernate/JPA sessions. + */ + void flush(); + } diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionCallbackWithoutResult.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionCallbackWithoutResult.java index 866ed94ce4c..44075a0977a 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionCallbackWithoutResult.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionCallbackWithoutResult.java @@ -1,12 +1,12 @@ /* - * Copyright 2002-2005 the original author or authors. - * + * Copyright 2002-2009 the original author or authors. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,7 +27,7 @@ import org.springframework.transaction.TransactionStatus; * @since 28.03.2003 * @see TransactionTemplate */ -public abstract class TransactionCallbackWithoutResult implements TransactionCallback { +public abstract class TransactionCallbackWithoutResult implements TransactionCallback { public final Object doInTransaction(TransactionStatus status) { doInTransactionWithoutResult(status); @@ -35,10 +35,10 @@ public abstract class TransactionCallbackWithoutResult implements TransactionCal } /** - * Gets called by TransactionTemplate.execute within a transactional context. - * Does not need to care about transactions itself, although it can retrieve - * and influence the status of the current transaction via the given status - * object, e.g. setting rollback-only. + * Gets called by TransactionTemplate.execute within a transactional + * context. Does not need to care about transactions itself, although it can retrieve + * and influence the status of the current transaction via the given status object, + * e.g. setting rollback-only. * *

A RuntimeException thrown by the callback is treated as application * exception that enforces a rollback. An exception gets propagated to the diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronization.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronization.java index 499ba022f65..a152f94698e 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronization.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronization.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,6 +60,13 @@ public interface TransactionSynchronization { */ void resume(); + /** + * Flush the underlying session to the datastore, if applicable: + * for example, a Hibernate/JPA session. + * @see org.springframework.transaction.TransactionStatus#flush() + */ + void flush(); + /** * Invoked before transaction commit (before "beforeCompletion"). * Can e.g. flush transactional O/R Mapping sessions to the database. diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronizationAdapter.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronizationAdapter.java index 7a1c4fd13f6..8ccdb536235 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronizationAdapter.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronizationAdapter.java @@ -1,12 +1,12 @@ /* - * Copyright 2002-2006 the original author or authors. - * + * Copyright 2002-2009 the original author or authors. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -42,6 +42,9 @@ public abstract class TransactionSynchronizationAdapter implements TransactionSy public void resume() { } + public void flush() { + } + public void beforeCommit(boolean readOnly) { } diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronizationUtils.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronizationUtils.java index 28777099bbe..e44c3c255f7 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronizationUtils.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/support/TransactionSynchronizationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,6 +59,17 @@ public abstract class TransactionSynchronizationUtils { } + /** + * Trigger flush callbacks on all currently registered synchronizations. + * @throws RuntimeException if thrown by a flush callback + * @see TransactionSynchronization#flush() + */ + public static void triggerFlush() { + for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { + synchronization.flush(); + } + } + /** * Trigger beforeCommit callbacks on all currently registered synchronizations. * @param readOnly whether the transaction is defined as read-only transaction @@ -121,7 +132,7 @@ public abstract class TransactionSynchronizationUtils { * @see TransactionSynchronization#STATUS_UNKNOWN */ public static void triggerAfterCompletion(int completionStatus) { - List synchronizations = TransactionSynchronizationManager.getSynchronizations(); + List synchronizations = TransactionSynchronizationManager.getSynchronizations(); invokeAfterCompletion(synchronizations, completionStatus); }