HHH-13262 - javax.persistence.TransactionRequiredException: Executing an update/delete query

This commit is contained in:
Andrea Boriero 2019-02-12 11:22:34 +00:00 committed by gbadner
parent 4256f300dd
commit ef3b07bd73
6 changed files with 41 additions and 22 deletions

View File

@ -157,6 +157,11 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
return delegate.isTransactionInProgress(); return delegate.isTransactionInProgress();
} }
@Override
public void checkTransactionNeededForUpdateOperation(String exceptionMessage) {
delegate.checkTransactionNeededForUpdateOperation( exceptionMessage );
}
@Override @Override
public LockOptions getLockRequest(LockModeType lockModeType, Map<String, Object> properties) { public LockOptions getLockRequest(LockModeType lockModeType, Map<String, Object> properties) {
return delegate.getLockRequest( lockModeType, properties ); return delegate.getLockRequest( lockModeType, properties );

View File

@ -12,6 +12,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.persistence.FlushModeType; import javax.persistence.FlushModeType;
import javax.persistence.TransactionRequiredException;
import org.hibernate.CacheMode; import org.hibernate.CacheMode;
import org.hibernate.Criteria; import org.hibernate.Criteria;
@ -180,6 +181,18 @@ public interface SharedSessionContractImplementor
*/ */
boolean isTransactionInProgress(); boolean isTransactionInProgress();
/**
* Check if an active Transaction is necessary for the update operation to be executed.
* If an active Transaction is necessary but it is not then a TransactionRequiredException is raised.
*
* @param exceptionMessage, the message to use for the TransactionRequiredException
*/
default void checkTransactionNeededForUpdateOperation(String exceptionMessage) {
if ( !isTransactionInProgress() ) {
throw getExceptionConverter().convert( new TransactionRequiredException( exceptionMessage ) );
}
}
/** /**
* Provides access to the underlying transaction or creates a new transaction if * Provides access to the underlying transaction or creates a new transaction if
* one does not already exist or is active. This is primarily for internal or * one does not already exist or is active. This is primarily for internal or

View File

@ -15,6 +15,7 @@ import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import javax.persistence.FlushModeType; import javax.persistence.FlushModeType;
import javax.persistence.TransactionRequiredException;
import javax.persistence.Tuple; import javax.persistence.Tuple;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
@ -128,6 +129,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
protected boolean closed; protected boolean closed;
protected boolean waitingForAutoClose; protected boolean waitingForAutoClose;
private transient boolean disallowOutOfTransactionUpdateOperations;
// transient & non-final for Serialization purposes - ugh // transient & non-final for Serialization purposes - ugh
private transient SessionEventListenerManagerImpl sessionEventsManager = new SessionEventListenerManagerImpl(); private transient SessionEventListenerManagerImpl sessionEventsManager = new SessionEventListenerManagerImpl();
@ -141,6 +143,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreationOptions options) { public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreationOptions options) {
this.factory = factory; this.factory = factory;
this.cacheTransactionSync = factory.getCache().getRegionFactory().createTransactionContext( this ); this.cacheTransactionSync = factory.getCache().getRegionFactory().createTransactionContext( this );
this.disallowOutOfTransactionUpdateOperations = !factory.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations();
this.flushMode = options.getInitialSessionFlushMode(); this.flushMode = options.getInitialSessionFlushMode();
@ -389,6 +392,13 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
return !isClosed() && transactionCoordinator.isTransactionActive(); return !isClosed() && transactionCoordinator.isTransactionActive();
} }
@Override
public void checkTransactionNeededForUpdateOperation(String exceptionMessage) {
if ( disallowOutOfTransactionUpdateOperations && !isTransactionInProgress() ) {
throw getExceptionConverter().convert( new TransactionRequiredException( exceptionMessage ) );
}
}
@Override @Override
public Transaction getTransaction() throws HibernateException { public Transaction getTransaction() throws HibernateException {
if ( getFactory().getSessionFactoryOptions().getJpaCompliance().isJpaTransactionComplianceEnabled() ) { if ( getFactory().getSessionFactoryOptions().getJpaCompliance().isJpaTransactionComplianceEnabled() ) {
@ -1133,5 +1143,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
entityNameResolver = new CoordinatingEntityNameResolver( factory, interceptor ); entityNameResolver = new CoordinatingEntityNameResolver( factory, interceptor );
exceptionConverter = new ExceptionConverterImpl( this ); exceptionConverter = new ExceptionConverterImpl( this );
this.disallowOutOfTransactionUpdateOperations = !getFactory().getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations();
} }
} }

View File

@ -242,7 +242,6 @@ public final class SessionImpl
private transient int dontFlushFromFind; private transient int dontFlushFromFind;
private transient boolean disallowOutOfTransactionUpdateOperations;
private transient ExceptionMapper exceptionMapper; private transient ExceptionMapper exceptionMapper;
private transient ManagedFlushChecker managedFlushChecker; private transient ManagedFlushChecker managedFlushChecker;
@ -266,7 +265,7 @@ public final class SessionImpl
this.autoClear = options.shouldAutoClear(); this.autoClear = options.shouldAutoClear();
this.autoClose = options.shouldAutoClose(); this.autoClose = options.shouldAutoClose();
this.queryParametersValidationEnabled = options.isQueryParametersValidationEnabled(); this.queryParametersValidationEnabled = options.isQueryParametersValidationEnabled();
this.disallowOutOfTransactionUpdateOperations = !factory.getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations();
this.discardOnClose = getFactory().getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled(); this.discardOnClose = getFactory().getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled();
if ( options instanceof SharedSessionCreationOptions && ( (SharedSessionCreationOptions) options ).isTransactionCoordinatorShared() ) { if ( options instanceof SharedSessionCreationOptions && ( (SharedSessionCreationOptions) options ).isTransactionCoordinatorShared() ) {
@ -1471,7 +1470,7 @@ public final class SessionImpl
} }
private void doFlush() { private void doFlush() {
checkTransactionNeeded(); checkTransactionNeededForUpdateOperation();
checkTransactionSynchStatus(); checkTransactionSynchStatus();
try { try {
@ -3536,7 +3535,7 @@ public final class SessionImpl
if ( lockModeType != null ) { if ( lockModeType != null ) {
if ( !LockModeType.NONE.equals( lockModeType) ) { if ( !LockModeType.NONE.equals( lockModeType) ) {
checkTransactionNeeded(); checkTransactionNeededForUpdateOperation();
} }
lockOptions = buildLockOptions( lockModeType, properties ); lockOptions = buildLockOptions( lockModeType, properties );
loadAccess.with( lockOptions ); loadAccess.with( lockOptions );
@ -3608,10 +3607,8 @@ public final class SessionImpl
return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE ); return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE );
} }
private void checkTransactionNeeded() { private void checkTransactionNeededForUpdateOperation() {
if ( disallowOutOfTransactionUpdateOperations && !isTransactionInProgress() ) { checkTransactionNeededForUpdateOperation( "no transaction is in progress" );
throw new TransactionRequiredException( "no transaction is in progress" );
}
} }
@Override @Override
@ -3637,7 +3634,7 @@ public final class SessionImpl
@Override @Override
public void lock(Object entity, LockModeType lockModeType, Map<String, Object> properties) { public void lock(Object entity, LockModeType lockModeType, Map<String, Object> properties) {
checkOpen(); checkOpen();
checkTransactionNeeded(); checkTransactionNeededForUpdateOperation();
if ( !contains( entity ) ) { if ( !contains( entity ) ) {
throw new IllegalArgumentException( "entity not in the persistence context" ); throw new IllegalArgumentException( "entity not in the persistence context" );
@ -3679,7 +3676,7 @@ public final class SessionImpl
if ( lockModeType != null ) { if ( lockModeType != null ) {
if ( !LockModeType.NONE.equals( lockModeType) ) { if ( !LockModeType.NONE.equals( lockModeType) ) {
checkTransactionNeeded(); checkTransactionNeededForUpdateOperation();
} }
lockOptions = buildLockOptions( lockModeType, properties ); lockOptions = buildLockOptions( lockModeType, properties );
@ -4011,7 +4008,6 @@ public final class SessionImpl
initializeFromSessionOwner( null ); initializeFromSessionOwner( null );
this.disallowOutOfTransactionUpdateOperations = !getFactory().getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations();
this.discardOnClose = getFactory().getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled(); this.discardOnClose = getFactory().getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled();
} }
} }

View File

@ -25,7 +25,6 @@ import javax.persistence.NonUniqueResultException;
import javax.persistence.Parameter; import javax.persistence.Parameter;
import javax.persistence.ParameterMode; import javax.persistence.ParameterMode;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import javax.persistence.TransactionRequiredException;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.ResultSetMappingDefinition;
@ -636,9 +635,8 @@ public class ProcedureCallImpl<R>
@Override @Override
public int executeUpdate() { public int executeUpdate() {
if ( ! getProducer().isTransactionInProgress() ) { getProducer().checkTransactionNeededForUpdateOperation(
throw new TransactionRequiredException( "javax.persistence.Query.executeUpdate requires active transaction" ); "javax.persistence.Query.executeUpdate requires active transaction" );
}
// the expectation is that there is just one Output, of type UpdateCountOutput // the expectation is that there is just one Output, of type UpdateCountOutput
try { try {

View File

@ -1606,13 +1606,8 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
@Override @Override
public int executeUpdate() throws HibernateException { public int executeUpdate() throws HibernateException {
if ( ! getProducer().isTransactionInProgress() ) { getProducer().checkTransactionNeededForUpdateOperation( "Executing an update/delete query" );
throw getProducer().getExceptionConverter().convert(
new TransactionRequiredException(
"Executing an update/delete query"
)
);
}
beforeQuery(); beforeQuery();
try { try {
return doExecuteUpdate(); return doExecuteUpdate();