Skip to content

Commit

Permalink
HHH-7090 - Temporary session closing affects original session
Browse files Browse the repository at this point in the history
  • Loading branch information
sebersole committed Mar 19, 2012
1 parent 60c1b23 commit 5a1d523
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 9 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -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().
Expand Down Expand Up @@ -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 );
}
Expand Down Expand Up @@ -282,7 +328,12 @@ public Connection close() throws HibernateException {
}

try {
return transactionCoordinator.close();
if ( !isTransactionCoordinatorShared ) {
return transactionCoordinator.close();
}
else {
return null; // ???
}
}
finally {
setClosed();
Expand Down Expand Up @@ -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) {
Expand Down
Expand Up @@ -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;

Expand All @@ -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;

/**
Expand All @@ -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();

Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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 };
Expand Down

0 comments on commit 5a1d523

Please sign in to comment.