- Add new configuration setting ALLOW_JTA_TRANSACTION_ACCESS. Uses can use this setting to

override default JPA behavior for transaction access if needed.
- Added new SPI method accessTransaction() which bypasses checks and returns the current or new transaction.
This commit is contained in:
Chris Cranford 2016-05-03 11:53:06 -05:00 committed by Steve Ebersole
parent f826b7d5e8
commit 0f2ced4668
12 changed files with 99 additions and 25 deletions

View File

@ -60,6 +60,7 @@ import org.hibernate.tuple.entity.EntityTuplizerFactory;
import org.jboss.logging.Logger;
import static org.hibernate.cfg.AvailableSettings.ACQUIRE_CONNECTIONS;
import static org.hibernate.cfg.AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS;
import static org.hibernate.cfg.AvailableSettings.AUTO_CLOSE_SESSION;
import static org.hibernate.cfg.AvailableSettings.AUTO_EVICT_COLLECTION_CACHE;
import static org.hibernate.cfg.AvailableSettings.AUTO_SESSION_EVENTS_LISTENER;
@ -480,6 +481,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
this.options.jpaBootstrap = true;
}
@Override
public void disableJtaTransactionAccess() {
this.options.jtaTransactionAccessEnabled = false;
}
@Override
public SessionFactoryOptions buildSessionFactoryOptions() {
return new SessionFactoryOptionsImpl( this );
@ -504,6 +510,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
// Session behavior
private boolean flushBeforeCompletionEnabled;
private boolean autoCloseSessionEnabled;
private boolean jtaTransactionAccessEnabled;
// (JTA) transaction handling
private boolean jtaTrackByThread;
@ -594,6 +601,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
BOOLEAN,
true
);
this.jtaTransactionAccessEnabled = cfgService.getSetting(
ALLOW_JTA_TRANSACTION_ACCESS,
BOOLEAN,
true
);
this.flushBeforeCompletionEnabled = cfgService.getSetting( FLUSH_BEFORE_COMPLETION, BOOLEAN, true );
this.autoCloseSessionEnabled = cfgService.getSetting( AUTO_CLOSE_SESSION, BOOLEAN, false );
@ -848,6 +860,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
return jpaBootstrap;
}
@Override
public boolean isJtaTransactionAccessEnabled() {
return jtaTransactionAccessEnabled;
}
@Override
public Object getBeanManagerReference() {
return beanManagerReference;
@ -1134,6 +1151,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
return options.isJpaBootstrap();
}
@Override
public boolean isJtaTransactionAccessEnabled() {
return options.isJtaTransactionAccessEnabled();
}
@Override
public Object getBeanManagerReference() {
return options.getBeanManagerReference();

View File

@ -51,6 +51,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
// Session behavior
private final boolean flushBeforeCompletionEnabled;
private final boolean autoCloseSessionEnabled;
private boolean jtaTransactionAccessEnabled;
// transaction handling
private final boolean jtaTrackByThread;
@ -124,6 +125,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
this.validatorFactoryReference = state.getValidatorFactoryReference();
this.jpaBootstrap = state.isJpaBootstrap();
this.jtaTransactionAccessEnabled = state.isJtaTransactionAccessEnabled();
this.sessionFactoryName = state.getSessionFactoryName();
this.sessionFactoryNameAlsoJndiName = state.isSessionFactoryNameAlsoJndiName();
@ -197,6 +199,11 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
return jpaBootstrap;
}
@Override
public boolean isJtaTransactionAccessEnabled() {
return jtaTransactionAccessEnabled;
}
@Override
public Object getBeanManagerReference() {
return beanManagerReference;

View File

@ -46,6 +46,8 @@ public interface SessionFactoryOptionsState {
@Deprecated
boolean isJpaBootstrap();
boolean isJtaTransactionAccessEnabled();
Object getBeanManagerReference();
Object getValidatorFactoryReference();

View File

@ -53,6 +53,11 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
return delegate.isJpaBootstrap();
}
@Override
public boolean isJtaTransactionAccessEnabled() {
return delegate.isJtaTransactionAccessEnabled();
}
@Override
public Object getBeanManagerReference() {
return delegate.getBeanManagerReference();

View File

@ -28,6 +28,8 @@ public interface SessionFactoryBuilderImplementor extends SessionFactoryBuilder
@Deprecated
void markAsJpaBootstrap();
void disableJtaTransactionAccess();
/**
* Build the SessionFactoryOptions that will ultimately be passed to SessionFactoryImpl constructor.
*

View File

@ -58,6 +58,8 @@ public interface SessionFactoryOptions {
@Deprecated
boolean isJpaBootstrap();
boolean isJtaTransactionAccessEnabled();
/**
* The name to be used for the SessionFactory. This is use both in:<ul>
* <li>in-VM serialization</li>

View File

@ -1463,4 +1463,15 @@ public interface AvailableSettings {
* @since 5.1
*/
String CREATE_EMPTY_COMPOSITES_ENABLED = "hibernate.create_empty_composites.enabled";
/**
* Setting that allows access to the underlying {@link org.hibernate.Transaction}, even
* when using a JTA since normal JPA operations prohibit this behavior.
* <p/>
* Values are {@code true} grants access, {@code false} does not.
* <p/>
* The default behavior is to allow access unless the session is bootstrapped via JPA.
*/
String ALLOW_JTA_TRANSACTION_ACCESS = "hibernate.jta.allowTransactionAccess";
}

View File

@ -433,6 +433,11 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
return delegate.getEventListenerManager();
}
@Override
public Transaction accessTransaction() {
return delegate.accessTransaction();
}
@Override
public Transaction beginTransaction() {
return delegate.beginTransaction();

View File

@ -21,6 +21,7 @@ import org.hibernate.Interceptor;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.SharedSessionContract;
import org.hibernate.Transaction;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
@ -150,6 +151,15 @@ public interface SharedSessionContractImplementor
*/
boolean isTransactionInProgress();
/**
* 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
* integrator use.
*
* @return the transaction
*/
Transaction accessTransaction();
/**
* Hide the changing requirements of entity key creation
*

View File

@ -352,37 +352,29 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
if ( getFactory().getSessionFactoryOptions().isJpaBootstrap() ) {
// JPA requires that we throw IllegalStateException if this is called
// on a JTA EntityManager
//
// todo : ultimately add an option for allowing users to access the Transaction in JTA cases too like classic Hibernate
if ( getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta() ) {
throw new IllegalStateException( "A JTA EntityManager cannot use getTransaction()" );
if ( !getFactory().getSessionFactoryOptions().isJtaTransactionAccessEnabled() ) {
throw new IllegalStateException( "A JTA EntityManager cannot use getTransaction()" );
}
}
if ( this.currentHibernateTransaction == null ) {
this.currentHibernateTransaction = new TransactionImpl(
getTransactionCoordinator(),
getExceptionConverter()
);
}
if ( !isClosed() ) {
getTransactionCoordinator().pulse();
}
return currentHibernateTransaction;
}
else {
// Historically Hibernate would not allow access to the Transaction after the Session is closed
checkOpen();
return accessTransaction();
}
if ( this.currentHibernateTransaction == null || this.currentHibernateTransaction.getStatus() != TransactionStatus.ACTIVE ) {
this.currentHibernateTransaction = new TransactionImpl(
getTransactionCoordinator(),
getExceptionConverter()
);
}
@Override
public Transaction accessTransaction() {
checkOpen();
if ( this.currentHibernateTransaction == null || this.currentHibernateTransaction.getStatus() != TransactionStatus.ACTIVE ) {
this.currentHibernateTransaction = new TransactionImpl(
getTransactionCoordinator(),
getExceptionConverter()
);
}
if ( !isClosed() ) {
getTransactionCoordinator().pulse();
return currentHibernateTransaction;
}
return this.currentHibernateTransaction;
}
@Override

View File

@ -346,4 +346,14 @@ public interface AvailableSettings {
* @since 5.0.8
*/
String DELAY_CDI_ACCESS = "hibernate.delay_cdi_access";
/**
* Setting that allows access to the underlying {@link org.hibernate.Transaction}, even
* when using a JTA since normal JPA operations prohibit this behavior.
* <p/>
* Values are {@code true} grants access, {@code false} does not.
* <p/>
* The default behavior is to allow access unless the session is bootstrapped via JPA.
*/
String ALLOW_JTA_TRANSACTION_ACCESS = "hibernate.jta.allowTransactionAccess";
}

View File

@ -881,6 +881,12 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
// sfBuilder.applyInterceptor( sessionFactoryInterceptor );
// }
// will use user override value or default to false if not supplied to follow JPA spec.
final boolean jtaTransactionAccessEnabled = readBooleanConfigurationValue( AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS );
if ( !jtaTransactionAccessEnabled ) {
( ( SessionFactoryBuilderImplementor ) sfBuilder ).disableJtaTransactionAccess();
}
// Locate and apply any requested SessionFactoryObserver
final Object sessionFactoryObserverSetting = configurationValues.remove( AvailableSettings.SESSION_FACTORY_OBSERVER );
if ( sessionFactoryObserverSetting != null ) {