diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/SessionEventListenerManagerImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/SessionEventListenerManagerImpl.java index ddd2b9220b2e..ec9a530643fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/SessionEventListenerManagerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/SessionEventListenerManagerImpl.java @@ -48,232 +48,190 @@ public void addListener(final SessionEventListener... additionalListeners) { @Override public void transactionCompletion(boolean successful) { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.transactionCompletion( successful ); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.transactionCompletion( successful ); + } } } @Override public void jdbcConnectionAcquisitionStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcConnectionAcquisitionStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcConnectionAcquisitionStart(); + } } } @Override public void jdbcConnectionAcquisitionEnd() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcConnectionAcquisitionEnd(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcConnectionAcquisitionEnd(); + } } } @Override public void jdbcConnectionReleaseStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcConnectionReleaseStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcConnectionReleaseStart(); + } } } @Override public void jdbcConnectionReleaseEnd() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcConnectionReleaseEnd(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcConnectionReleaseEnd(); + } } } @Override public void jdbcPrepareStatementStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcPrepareStatementStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcPrepareStatementStart(); + } } } @Override public void jdbcPrepareStatementEnd() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcPrepareStatementEnd(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcPrepareStatementEnd(); + } } } @Override public void jdbcExecuteStatementStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcExecuteStatementStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcExecuteStatementStart(); + } } } @Override public void jdbcExecuteStatementEnd() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcExecuteStatementEnd(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcExecuteStatementEnd(); + } } } @Override public void jdbcExecuteBatchStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcExecuteBatchStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcExecuteBatchStart(); + } } } @Override public void jdbcExecuteBatchEnd() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.jdbcExecuteBatchEnd(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.jdbcExecuteBatchEnd(); + } } } @Override public void cachePutStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.cachePutStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.cachePutStart(); + } } } @Override public void cachePutEnd() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.cachePutEnd(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.cachePutEnd(); + } } } @Override public void cacheGetStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.cacheGetStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.cacheGetStart(); + } } } @Override public void cacheGetEnd(boolean hit) { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.cacheGetEnd( hit ); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.cacheGetEnd( hit ); + } } } @Override public void flushStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.flushStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.flushStart(); + } } } @Override public void flushEnd(int numberOfEntities, int numberOfCollections) { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.flushEnd( numberOfEntities, numberOfCollections ); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.flushEnd( numberOfEntities, numberOfCollections ); + } } } @Override public void prePartialFlushStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.prePartialFlushStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.prePartialFlushStart(); + } } } @Override public void prePartialFlushEnd() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.prePartialFlushEnd(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.prePartialFlushEnd(); + } } } @Override public void partialFlushStart() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.partialFlushStart(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.partialFlushStart(); + } } } @Override public void partialFlushEnd(int numberOfEntities, int numberOfCollections) { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.partialFlushEnd( numberOfEntities, numberOfCollections ); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.partialFlushEnd( numberOfEntities, numberOfCollections ); + } } } @@ -290,23 +248,19 @@ public void dirtyCalculationStart() { @Override public void dirtyCalculationEnd(boolean dirty) { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.dirtyCalculationEnd( dirty ); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.dirtyCalculationEnd( dirty ); + } } } @Override public void end() { - if ( listeners == null ) { - return; - } - - for ( SessionEventListener listener : listeners ) { - listener.end(); + if ( listeners != null ) { + for ( SessionEventListener listener : listeners ) { + listener.end(); + } } } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 5a6c26fdc701..4a5a3cfec3cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -150,7 +150,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont private transient Transaction currentHibernateTransaction; private transient TransactionCoordinator transactionCoordinator; - private transient CacheTransactionSynchronization cacheTransactionSync; + private transient CacheTransactionSynchronization cacheTransactionSynchronization; private final boolean autoJoinTransactions; private final boolean isTransactionCoordinatorShared; @@ -185,7 +185,7 @@ public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreation this.factoryOptions = factory.getSessionFactoryOptions(); this.jdbcServices = factory.getJdbcServices(); - cacheTransactionSync = factory.getCache().getRegionFactory().createTransactionContext( this ); + cacheTransactionSynchronization = factory.getCache().getRegionFactory().createTransactionContext( this ); tenantIdentifier = getTenantId( factoryOptions, options ); interceptor = interpret( options.getInterceptor() ); jdbcTimeZone = options.getJdbcTimeZone(); @@ -310,7 +310,7 @@ private static SessionEventListenerManager createSessionEventsManager( public Integer getConfiguredJdbcBatchSize() { final Integer sessionJdbcBatchSize = jdbcBatchSize; return sessionJdbcBatchSize == null - ? getSessionFactoryOptions().getJdbcBatchSize() + ? factoryOptions.getJdbcBatchSize() : sessionJdbcBatchSize; } @@ -380,41 +380,41 @@ private StatementInspector interpret(StatementInspector statementInspector) { } @Override - public SessionFactoryImplementor getFactory() { + public final SessionFactoryImplementor getFactory() { return factory; } @Override - public Interceptor getInterceptor() { + public final Interceptor getInterceptor() { return interceptor; } @Override - public JdbcCoordinator getJdbcCoordinator() { + public final JdbcCoordinator getJdbcCoordinator() { return jdbcCoordinator; } @Override - public TransactionCoordinator getTransactionCoordinator() { + public final TransactionCoordinator getTransactionCoordinator() { return transactionCoordinator; } @Override - public JdbcSessionContext getJdbcSessionContext() { + public final JdbcSessionContext getJdbcSessionContext() { return jdbcSessionContext; } - public EntityNameResolver getEntityNameResolver() { + public final EntityNameResolver getEntityNameResolver() { return entityNameResolver; } @Override - public SessionEventListenerManager getEventListenerManager() { + public final SessionEventListenerManager getEventListenerManager() { return sessionEventsManager; } @Override - public UUID getSessionIdentifier() { + public final UUID getSessionIdentifier() { if ( sessionIdentifier == null ) { //Lazily initialized: otherwise all the UUID generations will cause significant amount of contention. sessionIdentifier = StandardRandomStrategy.INSTANCE.generateUUID( null ); @@ -423,7 +423,7 @@ public UUID getSessionIdentifier() { } @Override - public Object getSessionToken() { + public final Object getSessionToken() { if ( sessionToken == null ) { sessionToken = new Object(); } @@ -439,7 +439,7 @@ public String getTenantIdentifier() { } @Override - public Object getTenantIdentifierValue() { + public final Object getTenantIdentifierValue() { return tenantIdentifier; } @@ -460,7 +460,7 @@ public void close() { delayedAfterCompletion(); } catch ( HibernateException e ) { - if ( getSessionFactoryOptions().isJpaBootstrap() ) { + if ( factoryOptions.isJpaBootstrap() ) { throw getExceptionConverter().convert( e ); } else { @@ -503,7 +503,8 @@ protected void cleanupOnClose() { @Override public boolean isOpenOrWaitingForAutoClose() { - return !isClosed() || waitingForAutoClose; + return !closed && factory.isOpen() + || waitingForAutoClose; } @Override @@ -540,14 +541,13 @@ public void markForRollbackOnly() { @Override public boolean isTransactionInProgress() { - return waitingForAutoClose - ? factory.isOpen() && transactionCoordinator.isTransactionActive() - : !isClosed() && transactionCoordinator.isTransactionActive(); + return isOpenOrWaitingForAutoClose() + && transactionCoordinator.isTransactionActive(); } @Override public void checkTransactionNeededForUpdateOperation(String exceptionMessage) { - if ( !getSessionFactoryOptions().isAllowOutOfTransactionUpdateOperations() + if ( !factoryOptions.isAllowOutOfTransactionUpdateOperations() && !isTransactionInProgress() ) { throw new TransactionRequiredException( exceptionMessage ); } @@ -556,9 +556,8 @@ public void checkTransactionNeededForUpdateOperation(String exceptionMessage) { private boolean isTransactionAccessible() { // JPA requires that access not be provided to the transaction when using JTA. // This is overridden when SessionFactoryOptions isJtaTransactionAccessEnabled() is true. - final SessionFactoryOptions sessionFactoryOptions = getSessionFactoryOptions(); - return sessionFactoryOptions.isJtaTransactionAccessEnabled() // defaults to false in JPA bootstrap - || !sessionFactoryOptions.getJpaCompliance().isJpaTransactionComplianceEnabled() + return factoryOptions.isJtaTransactionAccessEnabled() // defaults to false in JPA bootstrap + || !factoryOptions.getJpaCompliance().isJpaTransactionComplianceEnabled() || !factory.transactionCoordinatorBuilder.isJta(); } @@ -577,30 +576,30 @@ public Transaction accessTransaction() { if ( currentHibernateTransaction == null ) { currentHibernateTransaction = new TransactionImpl( getTransactionCoordinator(), this ); } - if ( !isClosed() || waitingForAutoClose && factory.isOpen() ) { - getTransactionCoordinator().pulse(); + if ( isOpenOrWaitingForAutoClose() ) { + transactionCoordinator.pulse(); } return currentHibernateTransaction; } @Override public void startTransactionBoundary() { - getCacheTransactionSynchronization().transactionJoined(); + cacheTransactionSynchronization.transactionJoined(); } @Override public void beforeTransactionCompletion() { - getCacheTransactionSynchronization().transactionCompleting(); + cacheTransactionSynchronization.transactionCompleting(); } @Override public void afterTransactionCompletion(boolean successful, boolean delayed) { - getCacheTransactionSynchronization().transactionCompleted( successful ); + cacheTransactionSynchronization.transactionCompleted( successful ); } @Override - public CacheTransactionSynchronization getCacheTransactionSynchronization() { - return cacheTransactionSync; + public final CacheTransactionSynchronization getCacheTransactionSynchronization() { + return cacheTransactionSynchronization; } @Override @@ -637,26 +636,26 @@ protected void pulseTransactionCoordinator() { @Override public void joinTransaction() { checkOpen(); - if ( !getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta() ) { + if ( !transactionCoordinator.getTransactionCoordinatorBuilder().isJta() ) { log.callingJoinTransactionOnNonJtaEntityManager(); - return; } - - try { - getTransactionCoordinator().explicitJoin(); - } - catch ( TransactionRequiredForJoinException e ) { - throw new TransactionRequiredException( e.getMessage() ); - } - catch ( HibernateException he ) { - throw getExceptionConverter().convert( he ); + else { + try { + transactionCoordinator.explicitJoin(); + } + catch ( TransactionRequiredForJoinException e ) { + throw new TransactionRequiredException( e.getMessage() ); + } + catch ( HibernateException he ) { + throw getExceptionConverter().convert( he ); + } } } @Override public boolean isJoinedToTransaction() { checkOpen(); - return getTransactionCoordinator().isJoined(); + return transactionCoordinator.isJoined(); } protected void delayedAfterCompletion() { @@ -679,17 +678,17 @@ public boolean isConnected() { public JdbcConnectionAccess getJdbcConnectionAccess() { // See class-level JavaDocs for a discussion of the concurrent-access safety of this method if ( jdbcConnectionAccess == null ) { - if ( !getSessionFactoryOptions().isMultiTenancyEnabled() ) { + if ( !factoryOptions.isMultiTenancyEnabled() ) { jdbcConnectionAccess = new NonContextualJdbcConnectionAccess( - getEventListenerManager(), + sessionEventsManager, factory.connectionProvider, this ); } else { jdbcConnectionAccess = new ContextualJdbcConnectionAccess( - getTenantIdentifierValue(), - getEventListenerManager(), + tenantIdentifier, + sessionEventsManager, factory.multiTenantConnectionProvider, this ); @@ -704,28 +703,28 @@ public EntityKey generateEntityKey(Object id, EntityPersister persister) { } @Override - public SessionFactoryImplementor getSessionFactory() { + public final SessionFactoryImplementor getSessionFactory() { return factory; } @Override public boolean useStreamForLobBinding() { - return getJdbcServices().getJdbcEnvironment().getDialect().useInputStreamToInsertBlob(); + return getDialect().useInputStreamToInsertBlob(); } @Override public int getPreferredSqlTypeCodeForBoolean() { - return getSessionFactoryOptions().getPreferredSqlTypeCodeForBoolean(); + return factoryOptions.getPreferredSqlTypeCodeForBoolean(); } @Override public LobCreator getLobCreator() { - return getJdbcServices().getLobCreator( this ); + return jdbcServices.getLobCreator( this ); } @Override public Dialect getDialect() { - return getJdbcServices().getJdbcEnvironment().getDialect(); + return jdbcServices.getJdbcEnvironment().getDialect(); } @Override @@ -756,7 +755,7 @@ public TimeZone getJdbcTimeZone() { } @Override - public JdbcServices getJdbcServices() { + public final JdbcServices getJdbcServices() { return jdbcServices; } @@ -1579,12 +1578,12 @@ public void disableFilter(String filterName) { @Override public FormatMapper getXmlFormatMapper() { - return getSessionFactoryOptions().getXmlFormatMapper(); + return factoryOptions.getXmlFormatMapper(); } @Override public FormatMapper getJsonFormatMapper() { - return getSessionFactoryOptions().getJsonFormatMapper(); + return factoryOptions.getJsonFormatMapper(); } @Serial @@ -1647,7 +1646,7 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound jdbcSessionContext = createJdbcSessionContext( (StatementInspector) ois.readObject() ); jdbcCoordinator = JdbcCoordinatorImpl.deserialize( ois, this ); - cacheTransactionSync = factory.getCache().getRegionFactory().createTransactionContext( this ); + cacheTransactionSynchronization = factory.getCache().getRegionFactory().createTransactionContext( this ); transactionCoordinator = factory.transactionCoordinatorBuilder.buildTransactionCoordinator( jdbcCoordinator, this ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 02f25916ebf9..beb085109202 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -395,11 +395,16 @@ public void closeWithoutOpenChecks() { checkSessionFactoryOpen(); checkOpenOrWaitingForAutoClose(); if ( getSessionFactoryOptions().isReleaseResourcesOnCloseEnabled() - || !isTransactionInProgressAndNotMarkedForRollback() ) { + || !isTransactionInProgressAndNotMarkedForRollback() ) { super.close(); } else { - //Otherwise, session auto-close will be enabled by shouldAutoCloseSession(). + // In the JPA bootstrap, if the session is closed + // before the transaction commits, we just mark the + // session as closed, and set waitingForAutoClose. + // This method will be called a second time from + // afterTransactionCompletion when the transaction + // commits, and the session will be closed for real. prepareForAutoClose(); } } @@ -422,14 +427,8 @@ private boolean isJpaBootstrap() { } private boolean isTransactionInProgressAndNotMarkedForRollback() { - if ( waitingForAutoClose ) { - return getSessionFactory().isOpen() - && isTransactionActiveAndNotMarkedForRollback(); - } - else { - return !isClosed() - && isTransactionActiveAndNotMarkedForRollback(); - } + return isOpenOrWaitingForAutoClose() + && isTransactionActiveAndNotMarkedForRollback(); } private boolean isTransactionActiveAndNotMarkedForRollback() { @@ -482,7 +481,7 @@ protected void checkSessionFactoryOpen() { } private void managedFlush() { - if ( isClosed() && !waitingForAutoClose ) { + if ( !isOpenOrWaitingForAutoClose() ) { log.trace( "Skipping auto-flush since the session is closed" ); } else { @@ -2029,7 +2028,7 @@ public void afterTransactionCompletion(boolean successful, boolean delayed) { log.tracef( "SessionImpl#afterTransactionCompletion(successful=%s, delayed=%s)", successful, delayed ); } - final boolean notClosed = !isClosed() || waitingForAutoClose; + final boolean notClosed = isOpenOrWaitingForAutoClose(); if ( notClosed && (!successful || autoClear) ) { internalClear(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/autoclose/AutoCloseEntityManagerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/autoclose/AutoCloseEntityManagerTest.java new file mode 100644 index 000000000000..bff62b5f70c0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/autoclose/AutoCloseEntityManagerTest.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.autoclose; + +import jakarta.persistence.EntityManager; +import org.hibernate.SessionFactory; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Jpa +class AutoCloseEntityManagerTest { + @Test void testAutoClose(EntityManagerFactoryScope scope) { + SessionFactory factory = scope.getEntityManagerFactory().unwrap( SessionFactory.class ); + EntityManager session = factory.withOptions().autoClose( true ).openSession(); + var tx = session.getTransaction(); + tx.begin(); + tx.commit(); + assertFalse( session.isOpen() ); + } + + @Test void testNoAutoClose(EntityManagerFactoryScope scope) { + SessionFactory factory = scope.getEntityManagerFactory().unwrap( SessionFactory.class ); + EntityManager session = factory.withOptions().autoClose( false ).openSession(); + var tx = session.getTransaction(); + tx.begin(); + tx.commit(); + assertTrue( session.isOpen() ); + session.close(); + } + + @Test void testCloseWithTx(EntityManagerFactoryScope scope) { + EntityManager session = scope.getEntityManagerFactory().createEntityManager(); + var tx = session.getTransaction(); + tx.begin(); + // this is tolerated in JPA bootstrap + session.close(); + tx.commit(); + assertFalse( session.isOpen() ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/autoclose/AutoCloseSessionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/autoclose/AutoCloseSessionTest.java new file mode 100644 index 000000000000..b24c24b86183 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/autoclose/AutoCloseSessionTest.java @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.autoclose; + +import org.hibernate.Session; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SessionFactory +@DomainModel +class AutoCloseSessionTest { + @Test void testAutoClose(SessionFactoryScope scope) { + org.hibernate.SessionFactory factory = scope.getSessionFactory(); + Session session = factory.withOptions().autoClose( true ).openSession(); + var tx = session.beginTransaction(); + tx.commit(); + assertFalse( session.isOpen() ); + } + + @Test void testNoAutoClose(SessionFactoryScope scope) { + org.hibernate.SessionFactory factory = scope.getSessionFactory(); + Session session = factory.withOptions().autoClose( false ).openSession(); + var tx = session.beginTransaction(); + tx.commit(); + assertTrue( session.isOpen() ); + session.close(); + } + + @Test void testCloseWithTx(SessionFactoryScope scope) { + Session session = scope.getSessionFactory().openSession(); + var tx = session.beginTransaction(); + session.close(); + assertThrows( IllegalStateException.class, tx::commit ); + assertFalse( session.isOpen() ); + } +}