added "flush()" method to TransactionStatus and TransactionSynchronization interfaces; test context manager automatically flushes transactions before rolling back; general polishing of transaction management code

This commit is contained in:
Juergen Hoeller 2009-02-19 00:24:05 +00:00
parent dd7d299aa4
commit 4cc42bf16f
34 changed files with 415 additions and 134 deletions

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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() { public boolean isNewConnectionHolder() {
return newConnectionHolder; return this.newConnectionHolder;
} }
public boolean hasTransaction() { public boolean hasTransaction() {
@ -357,7 +357,7 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
} }
public boolean isMustRestoreAutoCommit() { public boolean isMustRestoreAutoCommit() {
return mustRestoreAutoCommit; return this.mustRestoreAutoCommit;
} }
public void setRollbackOnly() { public void setRollbackOnly() {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; return this.savepointAllowed;
} }
public void flush() {
// no-op
}
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// Implementation of SavepointManager // Implementation of SavepointManager

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 resume() {
} }
public void flush() {
}
public void beforeCommit(boolean readOnly) { public void beforeCommit(boolean readOnly) {
if (this.status != TransactionSynchronization.STATUS_COMMITTED) { if (this.status != TransactionSynchronization.STATUS_COMMITTED) {
fail("Should never be called"); fail("Should never be called");

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.support.ResourceHolder;
import org.springframework.transaction.support.ResourceHolderSynchronization; import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -387,7 +386,7 @@ public abstract class ConnectionFactoryUtils {
* (e.g. when participating in a JtaTransactionManager transaction). * (e.g. when participating in a JtaTransactionManager transaction).
* @see org.springframework.transaction.jta.JtaTransactionManager * @see org.springframework.transaction.jta.JtaTransactionManager
*/ */
private static class JmsResourceSynchronization extends ResourceHolderSynchronization { private static class JmsResourceSynchronization extends ResourceHolderSynchronization<JmsResourceHolder, Object> {
private final boolean transacted; private final boolean transacted;
@ -400,17 +399,17 @@ public abstract class ConnectionFactoryUtils {
return !this.transacted; return !this.transacted;
} }
protected void processResourceAfterCommit(ResourceHolder resourceHolder) { protected void processResourceAfterCommit(JmsResourceHolder resourceHolder) {
try { try {
((JmsResourceHolder) resourceHolder).commitAll(); resourceHolder.commitAll();
} }
catch (JMSException ex) { catch (JMSException ex) {
throw new SynchedLocalTransactionFailedException("Local JMS transaction failed to commit", ex); throw new SynchedLocalTransactionFailedException("Local JMS transaction failed to commit", ex);
} }
} }
protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { protected void releaseResource(JmsResourceHolder resourceHolder, Object resourceKey) {
((JmsResourceHolder) resourceHolder).closeAll(); resourceHolder.closeAll();
} }
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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() { public boolean isRollbackOnly() {
return this.resourceHolder.isRollbackOnly(); return this.resourceHolder.isRollbackOnly();
} }
public void flush() {
// no-op
}
} }
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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. * Check whether write operations are allowed on the given Session.
* <p>Default implementation throws an InvalidDataAccessApiUsageException in * <p>Default implementation throws an InvalidDataAccessApiUsageException in
* case of <code>FlushMode.NEVER/MANUAL</code>. Can be overridden in subclasses. * case of <code>FlushMode.MANUAL</code>. Can be overridden in subclasses.
* @param session current Hibernate Session * @param session current Hibernate Session
* @throws InvalidDataAccessApiUsageException if write operations are not allowed * @throws InvalidDataAccessApiUsageException if write operations are not allowed
* @see #setCheckWriteOperations * @see #setCheckWriteOperations
* @see #getFlushMode() * @see #getFlushMode()
* @see #FLUSH_EAGER * @see #FLUSH_EAGER
* @see org.hibernate.Session#getFlushMode() * @see org.hibernate.Session#getFlushMode()
* @see org.hibernate.FlushMode#NEVER
* @see org.hibernate.FlushMode#MANUAL * @see org.hibernate.FlushMode#MANUAL
*/ */
protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException { protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException {
if (isCheckWriteOperations() && getFlushMode() != FLUSH_EAGER && if (isCheckWriteOperations() && getFlushMode() != FLUSH_EAGER &&
session.getFlushMode().lessThan(FlushMode.COMMIT)) { session.getFlushMode().lessThan(FlushMode.COMMIT)) {
throw new InvalidDataAccessApiUsageException( 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."); "Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.");
} }
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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. * Hibernate transaction object, representing a SessionHolder.
* Used as transaction object by HibernateTransactionManager. * Used as transaction object by HibernateTransactionManager.
*/ */
private static class HibernateTransactionObject extends JdbcTransactionObjectSupport { private class HibernateTransactionObject extends JdbcTransactionObjectSupport {
private SessionHolder sessionHolder; private SessionHolder sessionHolder;
@ -875,16 +875,25 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
} }
public void setRollbackOnly() { public void setRollbackOnly() {
getSessionHolder().setRollbackOnly(); this.sessionHolder.setRollbackOnly();
if (hasConnectionHolder()) { if (hasConnectionHolder()) {
getConnectionHolder().setRollbackOnly(); getConnectionHolder().setRollbackOnly();
} }
} }
public boolean isRollbackOnly() { public boolean isRollbackOnly() {
return getSessionHolder().isRollbackOnly() || return this.sessionHolder.isRollbackOnly() ||
(hasConnectionHolder() && getConnectionHolder().isRollbackOnly()); (hasConnectionHolder() && getConnectionHolder().isRollbackOnly());
} }
public void flush() {
try {
this.sessionHolder.getSession().flush();
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 { public void beforeCommit(boolean readOnly) throws DataAccessException {
if (!readOnly) { if (!readOnly) {
Session session = getCurrentSession(); Session session = getCurrentSession();
@ -135,17 +145,21 @@ class SpringSessionSynchronization implements TransactionSynchronization, Ordere
session.flush(); session.flush();
} }
catch (HibernateException ex) { catch (HibernateException ex) {
if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) { throw translateException(ex);
JDBCException jdbcEx = (JDBCException) ex;
throw this.jdbcExceptionTranslator.translate(
"Hibernate flushing: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
}
throw SessionFactoryUtils.convertHibernateAccessException(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() { public void beforeCompletion() {
if (this.jtaTransaction != null) { if (this.jtaTransaction != null) {
// Typically in case of a suspended JTA transaction: // Typically in case of a suspended JTA transaction:

View File

@ -144,10 +144,10 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
* {@link org.hibernate.Session}. Only applied in single session mode. * {@link org.hibernate.Session}. Only applied in single session mode.
* <p>Can be populated with the corresponding constant name in XML bean * <p>Can be populated with the corresponding constant name in XML bean
* definitions: e.g. "AUTO". * definitions: e.g. "AUTO".
* <p>The default is "NEVER". Specify "AUTO" if you intend to use * <p>The default is "MANUAL". Specify "AUTO" if you intend to use
* this filter without service layer transactions. * this filter without service layer transactions.
* @see org.hibernate.Session#setFlushMode * @see org.hibernate.Session#setFlushMode
* @see org.hibernate.FlushMode#NEVER * @see org.hibernate.FlushMode#MANUAL
* @see org.hibernate.FlushMode#AUTO * @see org.hibernate.FlushMode#AUTO
*/ */
public void setFlushMode(FlushMode flushMode) { public void setFlushMode(FlushMode flushMode) {
@ -247,14 +247,14 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
* Note that this just applies in single session mode! * Note that this just applies in single session mode!
* <p>The default implementation delegates to the * <p>The default implementation delegates to the
* <code>SessionFactoryUtils.getSession</code> method and * <code>SessionFactoryUtils.getSession</code> method and
* sets the <code>Session</code>'s flush mode to "NEVER". * sets the <code>Session</code>'s flush mode to "MANUAL".
* <p>Can be overridden in subclasses for creating a Session with a * <p>Can be overridden in subclasses for creating a Session with a
* custom entity interceptor or JDBC exception translator. * custom entity interceptor or JDBC exception translator.
* @param sessionFactory the SessionFactory that this filter uses * @param sessionFactory the SessionFactory that this filter uses
* @return the Session to use * @return the Session to use
* @throws DataAccessResourceFailureException if the Session could not be created * @throws DataAccessResourceFailureException if the Session could not be created
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean) * @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 { protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true); Session session = SessionFactoryUtils.getSession(sessionFactory, true);

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 * Convert the given JDOException to an appropriate exception from the
* <code>org.springframework.dao</code> hierarchy. * <code>org.springframework.dao</code> hierarchy.
* <p>Default implementation delegates to the JdoDialect. * <p>The default implementation delegates to the JdoDialect.
* May be overridden in subclasses. * May be overridden in subclasses.
* @param ex JDOException that occured * @param ex JDOException that occured
* @return the corresponding DataAccessException instance * @return the corresponding DataAccessException instance
@ -518,7 +518,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
* JDO transaction object, representing a PersistenceManagerHolder. * JDO transaction object, representing a PersistenceManagerHolder.
* Used as transaction object by JdoTransactionManager. * Used as transaction object by JdoTransactionManager.
*/ */
private static class JdoTransactionObject extends JdbcTransactionObjectSupport { private class JdoTransactionObject extends JdbcTransactionObjectSupport {
private PersistenceManagerHolder persistenceManagerHolder; private PersistenceManagerHolder persistenceManagerHolder;
@ -533,11 +533,11 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
} }
public PersistenceManagerHolder getPersistenceManagerHolder() { public PersistenceManagerHolder getPersistenceManagerHolder() {
return persistenceManagerHolder; return this.persistenceManagerHolder;
} }
public boolean isNewPersistenceManagerHolder() { public boolean isNewPersistenceManagerHolder() {
return newPersistenceManagerHolder; return this.newPersistenceManagerHolder;
} }
public boolean hasTransaction() { public boolean hasTransaction() {
@ -550,7 +550,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
} }
public Object getTransactionData() { public Object getTransactionData() {
return transactionData; return this.transactionData;
} }
public void setRollbackOnly() { public void setRollbackOnly() {
@ -567,6 +567,15 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction(); Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction();
return tx.getRollbackOnly(); return tx.getRollbackOnly();
} }
public void flush() {
try {
this.persistenceManagerHolder.getPersistenceManager().flush();
}
catch (JDOException ex) {
throw convertJdoAccessException(ex);
}
}
} }

View File

@ -38,7 +38,6 @@ import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator; import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.transaction.support.ResourceHolder;
import org.springframework.transaction.support.ResourceHolderSynchronization; import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -293,7 +292,8 @@ public abstract class PersistenceManagerFactoryUtils {
* (e.g. when participating in a JtaTransactionManager transaction). * (e.g. when participating in a JtaTransactionManager transaction).
* @see org.springframework.transaction.jta.JtaTransactionManager * @see org.springframework.transaction.jta.JtaTransactionManager
*/ */
private static class PersistenceManagerSynchronization extends ResourceHolderSynchronization private static class PersistenceManagerSynchronization
extends ResourceHolderSynchronization<PersistenceManagerHolder, PersistenceManagerFactory>
implements Ordered { implements Ordered {
private final boolean newPersistenceManager; private final boolean newPersistenceManager;
@ -308,15 +308,24 @@ public abstract class PersistenceManagerFactoryUtils {
return PERSISTENCE_MANAGER_SYNCHRONIZATION_ORDER; return PERSISTENCE_MANAGER_SYNCHRONIZATION_ORDER;
} }
@Override
public void flushResource(PersistenceManagerHolder resourceHolder) {
try {
resourceHolder.getPersistenceManager().flush();
}
catch (JDOException ex) {
throw convertJdoAccessException(ex);
}
}
@Override @Override
protected boolean shouldUnbindAtCompletion() { protected boolean shouldUnbindAtCompletion() {
return this.newPersistenceManager; return this.newPersistenceManager;
} }
@Override @Override
protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { protected void releaseResource(PersistenceManagerHolder resourceHolder, PersistenceManagerFactory resourceKey) {
releasePersistenceManager(((PersistenceManagerHolder) resourceHolder).getPersistenceManager(), releasePersistenceManager(resourceHolder.getPersistenceManager(), resourceKey);
(PersistenceManagerFactory) resourceKey);
} }
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 // No matching persistence unit found - simply take the EntityManagerFactory
// with the persistence unit name as bean name (by convention). // 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). * (e.g. when participating in a JtaTransactionManager transaction).
* @see org.springframework.transaction.jta.JtaTransactionManager * @see org.springframework.transaction.jta.JtaTransactionManager
*/ */
private static class EntityManagerSynchronization extends ResourceHolderSynchronization implements Ordered { private static class EntityManagerSynchronization
extends ResourceHolderSynchronization<EntityManagerHolder, EntityManagerFactory>
implements Ordered {
private final Object transactionData; private final Object transactionData;
private final JpaDialect jpaDialect;
private final boolean newEntityManager; private final boolean newEntityManager;
public EntityManagerSynchronization( public EntityManagerSynchronization(
EntityManagerHolder emHolder, EntityManagerFactory emf, Object transactionData, boolean newEntityManager) { EntityManagerHolder emHolder, EntityManagerFactory emf, Object txData, boolean newEm) {
super(emHolder, emf); super(emHolder, emf);
this.transactionData = transactionData; this.transactionData = txData;
this.newEntityManager = newEntityManager; this.jpaDialect = (emf instanceof EntityManagerFactoryInfo ?
((EntityManagerFactoryInfo) emf).getJpaDialect() : null);
this.newEntityManager = newEm;
} }
public int getOrder() { public int getOrder() {
return ENTITY_MANAGER_SYNCHRONIZATION_ORDER; 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 @Override
protected boolean shouldUnbindAtCompletion() { protected boolean shouldUnbindAtCompletion() {
return this.newEntityManager; return this.newEntityManager;
} }
@Override @Override
protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { protected void releaseResource(EntityManagerHolder resourceHolder, EntityManagerFactory resourceKey) {
closeEntityManager(((EntityManagerHolder) resourceHolder).getEntityManager()); closeEntityManager(resourceHolder.getEntityManager());
} }
@Override @Override
protected void cleanupResource(ResourceHolder resourceHolder, Object resourceKey, boolean committed) { protected void cleanupResource(EntityManagerHolder resourceHolder, EntityManagerFactory resourceKey, boolean committed) {
if (!committed) { if (!committed) {
// Clear all pending inserts/updates/deletes in the EntityManager. // Clear all pending inserts/updates/deletes in the EntityManager.
// Necessary for pre-bound EntityManagers, to avoid inconsistent state. // 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);
} }
} }

View File

@ -436,7 +436,8 @@ public abstract class ExtendedEntityManagerCreator {
* TransactionSynchronization enlisting an extended EntityManager * TransactionSynchronization enlisting an extended EntityManager
* with a current Spring transaction. * with a current Spring transaction.
*/ */
private static class ExtendedEntityManagerSynchronization extends ResourceHolderSynchronization private static class ExtendedEntityManagerSynchronization
extends ResourceHolderSynchronization<EntityManagerHolder, EntityManager>
implements Ordered { implements Ordered {
private final EntityManager entityManager; private final EntityManager entityManager;
@ -454,6 +455,16 @@ public abstract class ExtendedEntityManagerCreator {
return EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER + 1; return EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER + 1;
} }
@Override
protected void flushResource(EntityManagerHolder resourceHolder) {
try {
this.entityManager.flush();
}
catch (RuntimeException ex) {
throw convertException(ex);
}
}
@Override @Override
protected boolean shouldReleaseBeforeCompletion() { protected boolean shouldReleaseBeforeCompletion() {
return false; return false;
@ -467,7 +478,7 @@ public abstract class ExtendedEntityManagerCreator {
this.entityManager.getTransaction().commit(); this.entityManager.getTransaction().commit();
} }
catch (RuntimeException ex) { catch (RuntimeException ex) {
throw convertCompletionException(ex); throw convertException(ex);
} }
} }
@ -480,12 +491,12 @@ public abstract class ExtendedEntityManagerCreator {
this.entityManager.getTransaction().rollback(); this.entityManager.getTransaction().rollback();
} }
catch (RuntimeException ex) { catch (RuntimeException ex) {
throw convertCompletionException(ex); throw convertException(ex);
} }
} }
} }
private RuntimeException convertCompletionException(RuntimeException ex) { private RuntimeException convertException(RuntimeException ex) {
DataAccessException daex = (this.exceptionTranslator != null) ? DataAccessException daex = (this.exceptionTranslator != null) ?
this.exceptionTranslator.translateExceptionIfPossible(ex) : this.exceptionTranslator.translateExceptionIfPossible(ex) :
EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex); EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex);

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction; import javax.persistence.EntityTransaction;
@ -553,7 +552,7 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
* JPA transaction object, representing a EntityManagerHolder. * JPA transaction object, representing a EntityManagerHolder.
* Used as transaction object by JpaTransactionManager. * Used as transaction object by JpaTransactionManager.
*/ */
private static class JpaTransactionObject extends JdbcTransactionObjectSupport { private class JpaTransactionObject extends JdbcTransactionObjectSupport {
private EntityManagerHolder entityManagerHolder; private EntityManagerHolder entityManagerHolder;
@ -606,6 +605,15 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
return tx.getRollbackOnly(); return tx.getRollbackOnly();
} }
public void flush() {
try {
this.entityManagerHolder.getEntityManager().flush();
}
catch (RuntimeException ex) {
throw DataAccessUtils.translateIfNecessary(ex, getJpaDialect());
}
}
@Override @Override
public Object createSavepoint() throws TransactionException { public Object createSavepoint() throws TransactionException {
return getSavepointManager().createSavepoint(); return getSavepointManager().createSavepoint();

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.ArrayList;
import java.util.List; import java.util.List;
import javax.transaction.RollbackException; import javax.transaction.RollbackException;
import javax.transaction.Status; import javax.transaction.Status;
import javax.transaction.Synchronization; import javax.transaction.Synchronization;
@ -108,7 +107,7 @@ public class HibernateJtaTransactionTests extends TestCase {
session.createQuery("some query string"); session.createQuery("some query string");
sessionControl.setReturnValue(query, 1); sessionControl.setReturnValue(query, 1);
if (readOnly) { if (readOnly) {
session.setFlushMode(FlushMode.NEVER); session.setFlushMode(FlushMode.MANUAL);
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
} }
query.list(); query.list();
@ -370,6 +369,14 @@ public class HibernateJtaTransactionTests extends TestCase {
} }
public void testJtaTransactionRollback() throws Exception { 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); MockControl utControl = MockControl.createControl(UserTransaction.class);
UserTransaction ut = (UserTransaction) utControl.getMock(); UserTransaction ut = (UserTransaction) utControl.getMock();
ut.getStatus(); ut.getStatus();
@ -390,6 +397,10 @@ public class HibernateJtaTransactionTests extends TestCase {
sfControl.setReturnValue(session, 1); sfControl.setReturnValue(session, 1);
session.getSessionFactory(); session.getSessionFactory();
sessionControl.setReturnValue(sf, 1); sessionControl.setReturnValue(sf, 1);
if (flush) {
session.flush();
sessionControl.setVoidCallable(1);
}
sfControl.replay(); sfControl.replay();
sessionControl.replay(); sessionControl.replay();
@ -409,6 +420,9 @@ public class HibernateJtaTransactionTests extends TestCase {
return l; return l;
} }
}); });
if (flush) {
status.flush();
}
status.setRollbackOnly(); status.setRollbackOnly();
sessionControl.verify(); sessionControl.verify();
sessionControl.reset(); sessionControl.reset();
@ -495,7 +509,7 @@ public class HibernateJtaTransactionTests extends TestCase {
sessionControl.setReturnValue(true, 5); sessionControl.setReturnValue(true, 5);
session.getFlushMode(); session.getFlushMode();
if (flushNever) { if (flushNever) {
sessionControl.setReturnValue(FlushMode.NEVER, 1); sessionControl.setReturnValue(FlushMode.MANUAL, 1);
if (!readOnly) { if (!readOnly) {
session.setFlushMode(FlushMode.AUTO); session.setFlushMode(FlushMode.AUTO);
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
@ -546,7 +560,7 @@ public class HibernateJtaTransactionTests extends TestCase {
session.flush(); session.flush();
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
if (flushNever) { if (flushNever) {
session.setFlushMode(FlushMode.NEVER); session.setFlushMode(FlushMode.MANUAL);
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
} }
} }
@ -1064,15 +1078,15 @@ public class HibernateJtaTransactionTests extends TestCase {
session.getSessionFactory(); session.getSessionFactory();
sessionControl.setReturnValue(sf, 1); sessionControl.setReturnValue(sf, 1);
session.getFlushMode(); session.getFlushMode();
sessionControl.setReturnValue(FlushMode.NEVER, 1); sessionControl.setReturnValue(FlushMode.MANUAL, 1);
session.setFlushMode(FlushMode.AUTO); session.setFlushMode(FlushMode.AUTO);
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
session.flush(); session.flush();
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
session.setFlushMode(FlushMode.NEVER); session.setFlushMode(FlushMode.MANUAL);
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
session.getFlushMode(); session.getFlushMode();
sessionControl.setReturnValue(FlushMode.NEVER, 1); sessionControl.setReturnValue(FlushMode.MANUAL, 1);
session.close(); session.close();
sessionControl.setReturnValue(null, 1); sessionControl.setReturnValue(null, 1);
sfControl.replay(); sfControl.replay();
@ -1306,7 +1320,7 @@ public class HibernateJtaTransactionTests extends TestCase {
sfControl.setReturnValue(tm, 7); sfControl.setReturnValue(tm, 7);
session.isOpen(); session.isOpen();
sessionControl.setReturnValue(true, 8); sessionControl.setReturnValue(true, 8);
session.setFlushMode(FlushMode.NEVER); session.setFlushMode(FlushMode.MANUAL);
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
session.close(); session.close();
sessionControl.setReturnValue(null, 2); sessionControl.setReturnValue(null, 2);
@ -1667,7 +1681,7 @@ public class HibernateJtaTransactionTests extends TestCase {
sessionControl.setReturnValue(true, 5); sessionControl.setReturnValue(true, 5);
session.getFlushMode(); session.getFlushMode();
if (flushNever) { if (flushNever) {
sessionControl.setReturnValue(FlushMode.NEVER, 1); sessionControl.setReturnValue(FlushMode.MANUAL, 1);
session.setFlushMode(FlushMode.AUTO); session.setFlushMode(FlushMode.AUTO);
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
} }
@ -1702,7 +1716,7 @@ public class HibernateJtaTransactionTests extends TestCase {
session.flush(); session.flush();
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
if (flushNever) { if (flushNever) {
session.setFlushMode(FlushMode.NEVER); session.setFlushMode(FlushMode.MANUAL);
sessionControl.setVoidCallable(1); sessionControl.setVoidCallable(1);
} }
session.disconnect(); session.disconnect();

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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()); 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() { protected void tearDown() {
assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty()); assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty());
assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); assertFalse(TransactionSynchronizationManager.isSynchronizationActive());

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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(); 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());
}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.lang.reflect.Proxy;
import java.util.List; import java.util.List;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException; import javax.persistence.EntityNotFoundException;
import javax.persistence.FlushModeType; import javax.persistence.FlushModeType;
@ -95,9 +94,8 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests
Person notThere = sharedEntityManager.getReference(Person.class, 666); Person notThere = sharedEntityManager.getReference(Person.class, 666);
// We may get here (as with Hibernate). // We may get here (as with Hibernate).
// Either behaviour is // Either behaviour is valid: throw exception on first access
// valid--throw exception on first access // or on getReference itself.
// or on getReference itself
notThere.getFirstName(); notThere.getFirstName();
} }
@ -175,8 +173,7 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests
} }
protected void testInstantiateAndSave(EntityManager em) { protected void testInstantiateAndSave(EntityManager em) {
assertEquals("Should be no people from previous transactions", assertEquals("Should be no people from previous transactions", 0, countRowsInTable("person"));
0, countRowsInTable("person"));
Person p = new Person(); Person p = new Person();
p.setFirstName("Tony"); p.setFirstName("Tony");
p.setLastName("Blair"); p.setLastName("Blair");

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.persist(p);
em.flush(); em.flush();
assertEquals("1 row must have been inserted", assertEquals("1 row must have been inserted", 1, countRowsInTable("person"));
1, countRowsInTable("person"));
} }
public void testStateClean() { public void testStateClean() {
assertEquals("Should be no people from previous transactions", assertEquals("Should be no people from previous transactions", 0, countRowsInTable("person"));
0, countRowsInTable("person"));
} }
public void testReuseInNewTransaction() { public void testReuseInNewTransaction() {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.persist(p);
em.flush(); em.flush();
assertEquals("1 row must have been inserted", assertEquals("1 row must have been inserted", 1, countRowsInTable("person"));
1, countRowsInTable("person"));
} }
public void testReuseInNewTransaction() { public void testReuseInNewTransaction() {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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(); 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();
}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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) { if (rollbackAnnotation != null) {
boolean rollbackOverride = rollbackAnnotation.value(); boolean rollbackOverride = rollbackAnnotation.value();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" +
+ "] for test context [" + testContext + "]"); rollback + "] for test context [" + testContext + "]");
} }
rollback = rollbackOverride; rollback = rollbackOverride;
} }
else { else {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("No method-level @Rollback override: using default rollback [" + rollback logger.debug("No method-level @Rollback override: using default rollback [" +
+ "] for test context [" + testContext + "]"); rollback + "] for test context [" + testContext + "]");
} }
} }
return rollback; return rollback;
@ -498,6 +498,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
public void endTransaction(boolean rollback) { public void endTransaction(boolean rollback) {
if (rollback) { if (rollback) {
this.transactionStatus.flush();
this.transactionManager.rollback(this.transactionStatus); this.transactionManager.rollback(this.transactionStatus);
} }
else { else {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.apache.commons.logging.LogFactory;
import org.springframework.jca.cci.CannotGetCciConnectionException; import org.springframework.jca.cci.CannotGetCciConnectionException;
import org.springframework.transaction.support.ResourceHolder;
import org.springframework.transaction.support.ResourceHolderSynchronization; import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert; 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 * Callback for resource cleanup at the end of a non-native CCI transaction
* (e.g. when participating in a JTA transaction). * (e.g. when participating in a JTA transaction).
*/ */
private static class ConnectionSynchronization extends ResourceHolderSynchronization { private static class ConnectionSynchronization
extends ResourceHolderSynchronization<ConnectionHolder, ConnectionFactory> {
public ConnectionSynchronization(ConnectionHolder connectionHolder, ConnectionFactory connectionFactory) { public ConnectionSynchronization(ConnectionHolder connectionHolder, ConnectionFactory connectionFactory) {
super(connectionHolder, connectionFactory); super(connectionHolder, connectionFactory);
} }
@Override @Override
protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) { protected void releaseResource(ConnectionHolder resourceHolder, ConnectionFactory resourceKey) {
releaseConnection(((ConnectionHolder) resourceHolder).getConnection(), (ConnectionFactory) resourceKey); releaseConnection(resourceHolder.getConnection(), resourceKey);
} }
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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(); 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, * Return whether this transaction is completed, that is,
* whether it has already been committed or rolled back. * whether it has already been committed or rolled back.

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.TransactionSystemException;
import org.springframework.transaction.support.SmartTransactionObject; import org.springframework.transaction.support.SmartTransactionObject;
import org.springframework.transaction.support.TransactionSynchronizationUtils;
/** /**
* JTA transaction object, representing a {@link javax.transaction.UserTransaction}. * 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();
}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; 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. * Mark this transaction as completed, that is, committed or rolled back.
*/ */

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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, * 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.
* <p>Will return "true" if the transaction itself has been marked rollback-only * <p>Will return "true" if the transaction itself has been marked rollback-only
* by the transaction coordinator, for example in case of a timeout. * by the transaction coordinator, for example in case of a timeout.
* @see SmartTransactionObject#isRollbackOnly * @see SmartTransactionObject#isRollbackOnly
@ -156,6 +156,16 @@ public class DefaultTransactionStatus extends AbstractTransactionStatus {
((SmartTransactionObject) this.transaction).isRollbackOnly()); ((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 * This implementation exposes the SavepointManager interface
* of the underlying transaction object, if any. * of the underlying transaction object, if any.

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 * @author Juergen Hoeller
* @since 2.5.5 * @since 2.5.5
*/ */
public class ResourceHolderSynchronization implements TransactionSynchronization { public abstract class ResourceHolderSynchronization<H extends ResourceHolder, K>
implements TransactionSynchronization {
private final ResourceHolder resourceHolder; private final H resourceHolder;
private final Object resourceKey; private final K resourceKey;
private volatile boolean holderActive = true; private volatile boolean holderActive = true;
@ -38,7 +39,7 @@ public class ResourceHolderSynchronization implements TransactionSynchronization
* @param resourceKey the key to bind the ResourceHolder for * @param resourceKey the key to bind the ResourceHolder for
* @see TransactionSynchronizationManager#bindResource * @see TransactionSynchronizationManager#bindResource
*/ */
public ResourceHolderSynchronization(ResourceHolder resourceHolder, Object resourceKey) { public ResourceHolderSynchronization(H resourceHolder, K resourceKey) {
this.resourceHolder = resourceHolder; this.resourceHolder = resourceHolder;
this.resourceKey = resourceKey; this.resourceKey = resourceKey;
} }
@ -56,6 +57,10 @@ public class ResourceHolderSynchronization implements TransactionSynchronization
} }
} }
public void flush() {
flushResource(this.resourceHolder);
}
public void beforeCommit(boolean readOnly) { public void beforeCommit(boolean readOnly) {
} }
@ -123,13 +128,20 @@ public class ResourceHolderSynchronization implements TransactionSynchronization
return true; 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. * After-commit callback for the given resource holder.
* Only called when the resource hasn't been released yet * Only called when the resource hasn't been released yet
* ({@link #shouldReleaseBeforeCompletion()}). * ({@link #shouldReleaseBeforeCompletion()}).
* @param resourceHolder the resource holder to process * @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 resourceHolder the resource holder to process
* @param resourceKey the key that the ResourceHolder was bound for * @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 (<code>true</code>) * @param committed whether the transaction has committed (<code>true</code>)
* or rolled back (<code>false</code>) * or rolled back (<code>false</code>)
*/ */
protected void cleanupResource(ResourceHolder resourceHolder, Object resourceKey, boolean committed) { protected void cleanupResource(H resourceHolder, K resourceKey, boolean committed) {
} }
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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. * indicating a new transaction.
*/ */
public SimpleTransactionStatus() { 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 * @param newTransaction whether to indicate a new transaction
*/ */
public SimpleTransactionStatus(boolean newTransaction) { public SimpleTransactionStatus(boolean newTransaction) {
@ -58,7 +58,7 @@ public class SimpleTransactionStatus extends AbstractTransactionStatus {
public boolean isNewTransaction() { public boolean isNewTransaction() {
return newTransaction; return this.newTransaction;
} }
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,4 +39,10 @@ public interface SmartTransactionObject {
*/ */
boolean isRollbackOnly(); boolean isRollbackOnly();
/**
* Flush the underlying sessions to the datastore, if applicable:
* for example, all affected Hibernate/JPA sessions.
*/
void flush();
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,7 +27,7 @@ import org.springframework.transaction.TransactionStatus;
* @since 28.03.2003 * @since 28.03.2003
* @see TransactionTemplate * @see TransactionTemplate
*/ */
public abstract class TransactionCallbackWithoutResult implements TransactionCallback { public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {
public final Object doInTransaction(TransactionStatus status) { public final Object doInTransaction(TransactionStatus status) {
doInTransactionWithoutResult(status); doInTransactionWithoutResult(status);
@ -35,10 +35,10 @@ public abstract class TransactionCallbackWithoutResult implements TransactionCal
} }
/** /**
* Gets called by TransactionTemplate.execute within a transactional context. * Gets called by <code>TransactionTemplate.execute</code> within a transactional
* Does not need to care about transactions itself, although it can retrieve * 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 * and influence the status of the current transaction via the given status object,
* object, e.g. setting rollback-only. * e.g. setting rollback-only.
* *
* <p>A RuntimeException thrown by the callback is treated as application * <p>A RuntimeException thrown by the callback is treated as application
* exception that enforces a rollback. An exception gets propagated to the * exception that enforces a rollback. An exception gets propagated to the

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -60,6 +60,13 @@ public interface TransactionSynchronization {
*/ */
void resume(); 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"). * Invoked before transaction commit (before "beforeCompletion").
* Can e.g. flush transactional O/R Mapping sessions to the database. * Can e.g. flush transactional O/R Mapping sessions to the database.

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -42,6 +42,9 @@ public abstract class TransactionSynchronizationAdapter implements TransactionSy
public void resume() { public void resume() {
} }
public void flush() {
}
public void beforeCommit(boolean readOnly) { public void beforeCommit(boolean readOnly) {
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -59,6 +59,17 @@ public abstract class TransactionSynchronizationUtils {
} }
/**
* Trigger <code>flush</code> callbacks on all currently registered synchronizations.
* @throws RuntimeException if thrown by a <code>flush</code> callback
* @see TransactionSynchronization#flush()
*/
public static void triggerFlush() {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.flush();
}
}
/** /**
* Trigger <code>beforeCommit</code> callbacks on all currently registered synchronizations. * Trigger <code>beforeCommit</code> callbacks on all currently registered synchronizations.
* @param readOnly whether the transaction is defined as read-only transaction * @param readOnly whether the transaction is defined as read-only transaction
@ -121,7 +132,7 @@ public abstract class TransactionSynchronizationUtils {
* @see TransactionSynchronization#STATUS_UNKNOWN * @see TransactionSynchronization#STATUS_UNKNOWN
*/ */
public static void triggerAfterCompletion(int completionStatus) { public static void triggerAfterCompletion(int completionStatus) {
List synchronizations = TransactionSynchronizationManager.getSynchronizations(); List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
invokeAfterCompletion(synchronizations, completionStatus); invokeAfterCompletion(synchronizations, completionStatus);
} }