mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-17 08:35:13 +00:00
HHH-7090 - Temporary session closing affects original session
This commit is contained in:
parent
60c1b23c7b
commit
5a1d523b4d
@ -100,6 +100,7 @@
|
||||
import org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl;
|
||||
import org.hibernate.engine.transaction.spi.TransactionCoordinator;
|
||||
import org.hibernate.engine.transaction.spi.TransactionImplementor;
|
||||
import org.hibernate.engine.transaction.spi.TransactionObserver;
|
||||
import org.hibernate.event.service.spi.EventListenerGroup;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.AutoFlushEvent;
|
||||
@ -196,6 +197,8 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
|
||||
|
||||
private transient LoadQueryInfluencers loadQueryInfluencers;
|
||||
|
||||
private final transient boolean isTransactionCoordinatorShared;
|
||||
|
||||
/**
|
||||
* Constructor used for openSession(...) processing, as well as construction
|
||||
* of sessions for getCurrentSession().
|
||||
@ -228,27 +231,70 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
|
||||
this.interceptor = interceptor == null ? EmptyInterceptor.INSTANCE : interceptor;
|
||||
this.actionQueue = new ActionQueue( this );
|
||||
this.persistenceContext = new StatefulPersistenceContext( this );
|
||||
this.flushBeforeCompletionEnabled = flushBeforeCompletionEnabled;
|
||||
|
||||
this.autoCloseSessionEnabled = autoCloseSessionEnabled;
|
||||
this.connectionReleaseMode = connectionReleaseMode;
|
||||
this.autoJoinTransactions = autoJoinTransactions;
|
||||
this.flushBeforeCompletionEnabled = flushBeforeCompletionEnabled;
|
||||
|
||||
if ( transactionCoordinator == null ) {
|
||||
this.transactionCoordinator = new TransactionCoordinatorImpl( connection, this );
|
||||
this.transactionCoordinator.getJdbcCoordinator().getLogicalConnection().addObserver(
|
||||
new ConnectionObserverStatsBridge( factory )
|
||||
);
|
||||
this.isTransactionCoordinatorShared = false;
|
||||
this.connectionReleaseMode = connectionReleaseMode;
|
||||
this.autoJoinTransactions = autoJoinTransactions;
|
||||
}
|
||||
else {
|
||||
if ( connection != null ) {
|
||||
throw new SessionException( "Cannot simultaneously share transaction context and specify connection" );
|
||||
}
|
||||
this.transactionCoordinator = transactionCoordinator;
|
||||
this.isTransactionCoordinatorShared = true;
|
||||
this.autoJoinTransactions = false;
|
||||
if ( autoJoinTransactions ) {
|
||||
LOG.debug(
|
||||
"Session creation specified 'autoJoinTransactions', which is invalid in conjunction " +
|
||||
"with sharing JDBC connection between sessions; ignoring"
|
||||
);
|
||||
}
|
||||
if ( connectionReleaseMode != transactionCoordinator.getJdbcCoordinator().getLogicalConnection().getConnectionReleaseMode() ) {
|
||||
LOG.debug(
|
||||
"Session creation specified 'connectionReleaseMode', which is invalid in conjunction " +
|
||||
"with sharing JDBC connection between sessions; ignoring"
|
||||
);
|
||||
}
|
||||
this.connectionReleaseMode = transactionCoordinator.getJdbcCoordinator().getLogicalConnection().getConnectionReleaseMode();
|
||||
|
||||
// add a transaction observer so that we can handle delegating managed actions back to THIS session
|
||||
// versus the session that created (and therefore "owns") the transaction coordinator
|
||||
transactionCoordinator.addObserver(
|
||||
new TransactionObserver() {
|
||||
@Override
|
||||
public void afterBegin(TransactionImplementor transaction) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeCompletion(TransactionImplementor transaction) {
|
||||
if ( SessionImpl.this.flushBeforeCompletionEnabled ) {
|
||||
SessionImpl.this.managedFlush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(boolean successful, TransactionImplementor transaction) {
|
||||
if ( SessionImpl.this.autoCloseSessionEnabled ) {
|
||||
SessionImpl.this.managedClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
loadQueryInfluencers = new LoadQueryInfluencers( factory );
|
||||
|
||||
if (factory.getStatistics().isStatisticsEnabled()) factory.getStatisticsImplementor().openSession();
|
||||
if (factory.getStatistics().isStatisticsEnabled()) {
|
||||
factory.getStatisticsImplementor().openSession();
|
||||
}
|
||||
|
||||
LOG.debugf( "Opened session at timestamp: %s", timestamp );
|
||||
}
|
||||
@ -282,7 +328,12 @@ public Connection close() throws HibernateException {
|
||||
}
|
||||
|
||||
try {
|
||||
return transactionCoordinator.close();
|
||||
if ( !isTransactionCoordinatorShared ) {
|
||||
return transactionCoordinator.close();
|
||||
}
|
||||
else {
|
||||
return null; // ???
|
||||
}
|
||||
}
|
||||
finally {
|
||||
setClosed();
|
||||
@ -348,8 +399,8 @@ public void applyNonFlushedChanges(NonFlushedChanges nonFlushedChanges) throws H
|
||||
errorIfClosed();
|
||||
checkTransactionSynchStatus();
|
||||
// todo : why aren't these just part of the NonFlushedChanges API ?
|
||||
replacePersistenceContext( ( ( NonFlushedChangesImpl ) nonFlushedChanges ).getPersistenceContext() );
|
||||
replaceActionQueue( ( ( NonFlushedChangesImpl ) nonFlushedChanges ).getActionQueue() );
|
||||
replacePersistenceContext( ((NonFlushedChangesImpl) nonFlushedChanges).getPersistenceContext() );
|
||||
replaceActionQueue( ((NonFlushedChangesImpl) nonFlushedChanges).getActionQueue() );
|
||||
}
|
||||
|
||||
private void replacePersistenceContext(StatefulPersistenceContext persistenceContextNew) {
|
||||
|
@ -27,6 +27,7 @@
|
||||
import org.hibernate.IrrelevantEntity;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.transaction.spi.TransactionContext;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@ -35,6 +36,8 @@
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
@ -44,7 +47,7 @@ public class SessionWithSharedConnectionTest extends BaseCoreFunctionalTestCase
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-7020" )
|
||||
@FailureExpected( jiraKey = "HHH-7020" )
|
||||
public void testSharedTransactionContextSessionClosing() {
|
||||
public void testSharedConnectionSessionClosing() {
|
||||
Session session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
|
||||
@ -82,7 +85,7 @@ public void testSharedTransactionContextSessionClosing() {
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-7020" )
|
||||
@FailureExpected( jiraKey = "HHH-7020" )
|
||||
public void testSharedTransactionContextAutoClosing() {
|
||||
public void testSharedConnectionAutoClosing() {
|
||||
Session session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
|
||||
@ -116,6 +119,149 @@ public void testSharedTransactionContextAutoClosing() {
|
||||
assertTrue( ((SessionImplementor) secondSession).isClosed() );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-7090" )
|
||||
public void testSharedTransactionContextSessionClosing() {
|
||||
Session session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
|
||||
Session secondSession = session.sessionWithOptions()
|
||||
.transactionContext()
|
||||
.openSession();
|
||||
secondSession.createCriteria( IrrelevantEntity.class ).list();
|
||||
|
||||
//the list should have registered and then released a JDBC resource
|
||||
assertFalse(
|
||||
((SessionImplementor) secondSession).getTransactionCoordinator()
|
||||
.getJdbcCoordinator()
|
||||
.getLogicalConnection()
|
||||
.getResourceRegistry()
|
||||
.hasRegisteredResources()
|
||||
);
|
||||
|
||||
assertTrue( session.isOpen() );
|
||||
assertTrue( secondSession.isOpen() );
|
||||
|
||||
assertSame( session.getTransaction(), secondSession.getTransaction() );
|
||||
|
||||
session.getTransaction().commit();
|
||||
|
||||
assertTrue( session.isOpen() );
|
||||
assertTrue( secondSession.isOpen() );
|
||||
|
||||
secondSession.close();
|
||||
assertTrue( session.isOpen() );
|
||||
assertFalse( secondSession.isOpen() );
|
||||
|
||||
session.close();
|
||||
assertFalse( session.isOpen() );
|
||||
assertFalse( secondSession.isOpen() );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-7090" )
|
||||
public void testSharedTransactionContextAutoClosing() {
|
||||
Session session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
|
||||
// COMMIT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Session secondSession = session.sessionWithOptions()
|
||||
.transactionContext()
|
||||
.autoClose( true )
|
||||
.openSession();
|
||||
|
||||
// directly assert state of the second session
|
||||
assertTrue( ((TransactionContext) secondSession).isAutoCloseSessionEnabled() );
|
||||
assertTrue( ((TransactionContext) secondSession).shouldAutoClose() );
|
||||
|
||||
// now commit the transaction and make sure that does not close the sessions
|
||||
session.getTransaction().commit();
|
||||
assertFalse( ((SessionImplementor) session).isClosed() );
|
||||
assertTrue( ((SessionImplementor) secondSession).isClosed() );
|
||||
|
||||
session.close();
|
||||
assertTrue( ((SessionImplementor) session).isClosed() );
|
||||
assertTrue( ((SessionImplementor) secondSession).isClosed() );
|
||||
|
||||
|
||||
// ROLLBACK ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
|
||||
secondSession = session.sessionWithOptions()
|
||||
.transactionContext()
|
||||
.autoClose( true )
|
||||
.openSession();
|
||||
|
||||
// directly assert state of the second session
|
||||
assertTrue( ((TransactionContext) secondSession).isAutoCloseSessionEnabled() );
|
||||
assertTrue( ((TransactionContext) secondSession).shouldAutoClose() );
|
||||
|
||||
// now rollback the transaction and make sure that does not close the sessions
|
||||
session.getTransaction().rollback();
|
||||
assertFalse( ((SessionImplementor) session).isClosed() );
|
||||
assertTrue( ((SessionImplementor) secondSession).isClosed() );
|
||||
|
||||
session.close();
|
||||
assertTrue( ((SessionImplementor) session).isClosed() );
|
||||
assertTrue( ((SessionImplementor) secondSession).isClosed() );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-7090" )
|
||||
public void testSharedTransactionContextAutoJoining() {
|
||||
Session session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
|
||||
Session secondSession = session.sessionWithOptions()
|
||||
.transactionContext()
|
||||
.autoJoinTransactions( true )
|
||||
.openSession();
|
||||
|
||||
// directly assert state of the second session
|
||||
assertFalse( ((TransactionContext) secondSession).shouldAutoJoinTransaction() );
|
||||
|
||||
secondSession.close();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-7090" )
|
||||
public void testSharedTransactionContextFlushBeforeCompletion() {
|
||||
Session session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
|
||||
Session secondSession = session.sessionWithOptions()
|
||||
.transactionContext()
|
||||
.flushBeforeCompletion( true )
|
||||
.autoClose( true )
|
||||
.openSession();
|
||||
|
||||
// directly assert state of the second session
|
||||
assertTrue( ((TransactionContext) secondSession).isFlushBeforeCompletionEnabled() );
|
||||
|
||||
// now try it out
|
||||
Integer id = (Integer) secondSession.save( new IrrelevantEntity() );
|
||||
session.getTransaction().commit();
|
||||
assertFalse( ((SessionImplementor) session).isClosed() );
|
||||
assertTrue( ((SessionImplementor) secondSession).isClosed() );
|
||||
|
||||
session.close();
|
||||
assertTrue( ((SessionImplementor) session).isClosed() );
|
||||
assertTrue( ((SessionImplementor) secondSession).isClosed() );
|
||||
|
||||
session = sessionFactory().openSession();
|
||||
session.getTransaction().begin();
|
||||
IrrelevantEntity it = (IrrelevantEntity) session.byId( IrrelevantEntity.class ).load( id );
|
||||
assertNotNull( it );
|
||||
session.delete( it );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { IrrelevantEntity.class };
|
||||
|
Loading…
x
Reference in New Issue
Block a user