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-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.
* <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
* @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.");
}
}

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");
* 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);
}
}
}

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");
* 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:

View File

@@ -144,10 +144,10 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
* {@link org.hibernate.Session}. Only applied in single session mode.
* <p>Can be populated with the corresponding constant name in XML bean
* 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.
* @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!
* <p>The default implementation delegates to the
* <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
* 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);

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");
* 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
* <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.
* @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);
}
}
}

View File

@@ -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<PersistenceManagerHolder, PersistenceManagerFactory>
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);
}
}

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");
* 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<EntityManagerHolder, EntityManagerFactory>
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);
}
}

View File

@@ -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<EntityManagerHolder, EntityManager>
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);

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");
* 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();