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:
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user