/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.sharedSession;
import org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl;
import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.*;
import org.hibernate.testing.FailureExpected;
import org.junit.Test;
import org.hibernate.IrrelevantEntity;
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.transaction.spi.TransactionContext;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import java.lang.reflect.Field;
import java.util.List;
import static org.junit.Assert.*;
/**
* @author Steve Ebersole
*/
public class SessionWithSharedConnectionTest extends BaseCoreFunctionalTestCase {
@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();
}
@Test
@TestForIssue( jiraKey = "HHH-7239" )
public void testSessionRemovedFromObserversOnClose() throws Exception {
Session session = sessionFactory().openSession();
session.getTransaction().begin();
//get the initial count of observers (use reflection as the observers property isn't exposed)
Field field = TransactionCoordinatorImpl.class.getDeclaredField( "observers" );
field.setAccessible(true);
List observers = (List) field.get( ( ( SessionImplementor ) session ).getTransactionCoordinator() );
int originalObserverSize = observers.size();
//opening 2nd session registers it with the TransactionCoordinator currently as an observer
Session secondSession = session.sessionWithOptions()
.connection()
.flushBeforeCompletion( false )
.autoClose( false )
.openSession();
observers = (List) field.get( ( ( SessionImplementor ) session ).getTransactionCoordinator() );
//the observer size should be larger
final int observerSizeWithSecondSession = observers.size();
assertTrue( observerSizeWithSecondSession > originalObserverSize);
//don't need to actually even do anything with the 2nd session
secondSession.close();
//the second session should be released from the observers on close since it didn't have any after transaction actions
observers = (List) field.get( ( ( SessionImplementor ) session ).getTransactionCoordinator() );
assertEquals( originalObserverSize, observers.size() );
//store the transaction coordinator here since it's not available after session close
TransactionCoordinator transactionCoordinator = ((SessionImplementor) session).getTransactionCoordinator();
session.getTransaction().commit();
session.close();
//on original session close all observers should be released
observers = (List) field.get( transactionCoordinator );
assertEquals( 0, observers.size() );
}
@Test
@TestForIssue( jiraKey = "HHH-7239" )
public void testChildSessionCallsAfterTransactionAction() throws Exception {
Session session = openSession();
final String postCommitMessage = "post commit was called";
EventListenerRegistry eventListenerRegistry = sessionFactory().getServiceRegistry().getService(EventListenerRegistry.class);
//register a post commit listener
eventListenerRegistry.appendListeners( EventType.POST_COMMIT_INSERT, new PostInsertEventListener(){
@Override
public void onPostInsert(PostInsertEvent event) {
((IrrelevantEntity) event.getEntity()).setName( postCommitMessage );
}
});
session.getTransaction().begin();
IrrelevantEntity irrelevantEntityMainSession = new IrrelevantEntity();
irrelevantEntityMainSession.setName( "main session" );
session.save( irrelevantEntityMainSession );
//open secondary session to also insert an entity
Session secondSession = session.sessionWithOptions()
.connection()
.flushBeforeCompletion( true )
.autoClose( true )
.openSession();
IrrelevantEntity irrelevantEntitySecondarySession = new IrrelevantEntity();
irrelevantEntitySecondarySession.setName( "secondary session" );
secondSession.save( irrelevantEntitySecondarySession );
session.getTransaction().commit();
//both entities should have their names updated to the postCommitMessage value
assertEquals(postCommitMessage, irrelevantEntityMainSession.getName());
assertEquals(postCommitMessage, irrelevantEntitySecondarySession.getName());
}
@Test
@TestForIssue( jiraKey = "HHH-7239" )
public void testChildSessionTwoTransactions() throws Exception {
Session session = openSession();
session.getTransaction().begin();
//open secondary session with managed options
Session secondarySession = session.sessionWithOptions()
.connection()
.flushBeforeCompletion( true )
.autoClose( true )
.openSession();
//the secondary session should be automatically closed after the commit
session.getTransaction().commit();
assertFalse( secondarySession.isOpen() );
//should be able to create a new transaction and carry on using the original session
session.getTransaction().begin();
session.getTransaction().commit();
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { IrrelevantEntity.class };
}
}