HHH-5981 - Clarify Session.disconnect() and Session.reconnect() behavior

This commit is contained in:
Steve Ebersole 2011-03-04 12:43:43 -06:00
parent 4d5b9f1ca1
commit 4f4f374dae
12 changed files with 70 additions and 133 deletions

View File

@ -920,39 +920,25 @@ public interface Session extends Serializable {
public void doWork(Work work) throws HibernateException;
/**
* Disconnect the <tt>Session</tt> from the current JDBC connection. If
* the connection was obtained by Hibernate close it and return it to
* the connection pool; otherwise, return it to the application.
* Disconnect the session from its underlying JDBC connection. This is intended for use in cases where the
* application has supplied the JDBC connection to the session and which require long-sessions (aka, conversations).
* <p/>
* This is used by applications which supply JDBC connections to Hibernate
* and which require long-sessions (or long-conversations)
* It is considered an error to call this method on a session which was not opened by supplying the JDBC connection
* and an exception will be thrown.
* <p/>
* Note that disconnect() called on a session where the connection was
* retrieved by Hibernate through its configured
* {@link org.hibernate.service.jdbc.connections.spi.ConnectionProvider} has no effect,
* provided {@link ConnectionReleaseMode#ON_CLOSE} is not in effect.
* For non-user-supplied scenarios, normal transaction management already handles disconnection and reconnection
* automatically.
*
* @return the application-supplied connection or <tt>null</tt>
* @return the application-supplied connection or {@literal null}
*
* @see SessionFactory#openSession(java.sql.Connection)
* @see SessionFactory#openSession(java.sql.Connection, Interceptor)
* @see #reconnect(Connection)
* @see #reconnect()
*/
Connection disconnect() throws HibernateException;
/**
* Obtain a new JDBC connection. This is used by applications which
* require long transactions and do not supply connections to the
* session.
*
* @see #disconnect()
* @deprecated Manual reconnection is only needed in the case of
* application-supplied connections, in which case the
* {@link #reconnect(java.sql.Connection)} for should be used.
*/
void reconnect() throws HibernateException;
/**
* Reconnect to the given JDBC connection. This is used by applications
* which require long transactions and use application-supplied connections.
* Reconnect to the given JDBC connection.
*
* @param connection a JDBC connection
* @see #disconnect()

View File

@ -331,6 +331,15 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
releaseNonDurableObservers();
}
private void releaseNonDurableObservers() {
Iterator observers = this.observers.iterator();
while ( observers.hasNext() ) {
if ( NonDurableConnectionObserver.class.isInstance( observers.next() ) ) {
observers.remove();
}
}
}
@Override
public Connection manualDisconnect() {
if ( isClosed ) {
@ -343,26 +352,20 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
return c;
}
private void releaseNonDurableObservers() {
Iterator observers = this.observers.iterator();
while ( observers.hasNext() ) {
if ( NonDurableConnectionObserver.class.isInstance( observers.next() ) ) {
observers.remove();
}
}
}
@Override
public void manualReconnect(Connection suppliedConnection) {
if ( isClosed ) {
throw new IllegalStateException( "cannot manually reconnect because logical connection is already closed" );
}
if ( isUserSuppliedConnection ) {
if ( !isUserSuppliedConnection ) {
throw new IllegalStateException( "cannot manually reconnect unless Connection was originally supplied" );
}
else {
if ( suppliedConnection == null ) {
throw new IllegalArgumentException( "cannot reconnect a null user-supplied connection" );
}
else if ( suppliedConnection == physicalConnection ) {
log.warn( "reconnecting the same connection that is already connected; should this connection have been disconnected?" );
log.debug( "reconnecting the same connection that is already connected; should this connection have been disconnected?" );
}
else if ( physicalConnection != null ) {
throw new IllegalArgumentException(
@ -372,12 +375,6 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
physicalConnection = suppliedConnection;
log.debug( "reconnected JDBC connection" );
}
else {
if ( suppliedConnection != null ) {
throw new IllegalStateException( "unexpected user-supplied connection" );
}
log.debug( "called reconnect() with null connection (not user-supplied)" );
}
}
@Override

View File

@ -41,6 +41,7 @@ import org.hibernate.engine.query.NativeSQLQueryPlan;
import org.hibernate.engine.transaction.spi.TransactionContext;
import org.hibernate.engine.transaction.spi.TransactionEnvironment;
import java.io.Serializable;
import java.util.List;
/**
@ -48,7 +49,7 @@ import java.util.List;
*
* @author Gavin King
*/
public abstract class AbstractSessionImpl implements SessionImplementor, TransactionContext {
public abstract class AbstractSessionImpl implements Serializable, SessionImplementor, TransactionContext {
protected transient SessionFactoryImpl factory;
private boolean closed = false;

View File

@ -544,19 +544,14 @@ public final class SessionImpl
return !isClosed() && transactionCoordinator.isTransactionInProgress();
}
@Override
public Connection disconnect() throws HibernateException {
errorIfClosed();
log.debug( "disconnecting session" );
return transactionCoordinator.getJdbcCoordinator().getLogicalConnection().manualDisconnect();
}
public void reconnect() throws HibernateException {
errorIfClosed();
log.debug( "reconnecting session" );
checkTransactionSynchStatus();
transactionCoordinator.getJdbcCoordinator().getLogicalConnection().manualReconnect( null );
}
@Override
public void reconnect(Connection conn) throws HibernateException {
errorIfClosed();
log.debug( "reconnecting session" );

View File

@ -53,8 +53,8 @@ public class AggressiveReleaseTest extends ConnectionManagementTestCase {
return openSession();
}
@Override
protected void reconnect(Session session) {
session.reconnect();
}
protected void prepare() throws Throwable {

View File

@ -29,7 +29,6 @@ public class BasicConnectionProviderTest extends ConnectionManagementTestCase {
}
protected void reconnect(Session session) {
session.reconnect();
}
public void configure(Configuration cfg) {

View File

@ -75,6 +75,10 @@ public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
}
}
protected void disconnect(Session session) throws Throwable {
session.disconnect();
}
/**
* Perform any steps needed to reconnect a fixture session.
*
@ -139,7 +143,7 @@ public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
sessionUnderTest.enableFilter( "nameIsNull" );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
sessionUnderTest.disconnect();
disconnect( sessionUnderTest );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
byte[] bytes = SerializationHelper.serialize( sessionUnderTest );
@ -147,7 +151,7 @@ public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
reconnect( sessionUnderTest );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
sessionUnderTest.disconnect();
disconnect( sessionUnderTest );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
Session s2 = ( Session ) SerializationHelper.deserialize( bytes );
@ -156,7 +160,7 @@ public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
reconnect( s2 );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
s2.disconnect();
disconnect( s2 );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
reconnect( s2 );
assertNotNull( sessionUnderTest.getEnabledFilter( "nameIsNull" ) );
@ -174,7 +178,7 @@ public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
prepare();
Session sessionUnderTest = getSessionUnderTest();
sessionUnderTest.disconnect();
disconnect( sessionUnderTest );
SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );
@ -191,7 +195,7 @@ public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
prepare();
Session sessionUnderTest = getSessionUnderTest();
sessionUnderTest.disconnect();
disconnect( sessionUnderTest );
byte[] bytes = SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );
@ -200,7 +204,7 @@ public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
reconnect( s2 );
s2.disconnect();
disconnect( s2 );
reconnect( s2 );
release( sessionUnderTest );
@ -224,14 +228,14 @@ public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
sessionUnderTest.createQuery( "from Silly" ).iterate();
sessionUnderTest.disconnect();
disconnect( sessionUnderTest );
SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );
reconnect( sessionUnderTest );
sessionUnderTest.createQuery( "from Silly" ).scroll();
sessionUnderTest.disconnect();
disconnect( sessionUnderTest );
SerializationHelper.serialize( sessionUnderTest );
checkSerializedState( sessionUnderTest );

View File

@ -8,6 +8,7 @@ import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.context.ThreadLocalSessionContext;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.transaction.spi.LocalStatus;
import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite;
/**
@ -36,6 +37,10 @@ public class ThreadLocalCurrentSessionTest extends ConnectionManagementTestCase
}
protected void release(Session session) {
if ( session.getTransaction().getLocalStatus() != LocalStatus.ACTIVE ) {
TestableThreadLocalContext.unbind( sfi() );
return;
}
long initialCount = getSessions().getStatistics().getSessionCloseCount();
session.getTransaction().commit();
long subsequentCount = getSessions().getStatistics().getSessionCloseCount();
@ -45,8 +50,6 @@ public class ThreadLocalCurrentSessionTest extends ConnectionManagementTestCase
}
protected void reconnect(Session session) throws Throwable {
// session.reconnect();
session.beginTransaction();
}
protected void checkSerializedState(Session session) {

View File

@ -1358,9 +1358,7 @@ public class FooBarTest extends LegacyTestCase {
Object b = result[0];
assertTrue( s.getCurrentLockMode(b)==LockMode.WRITE && s.getCurrentLockMode( result[1] )==LockMode.WRITE );
tx.commit();
s.disconnect();
s.reconnect();
tx = s.beginTransaction();
assertTrue( s.getCurrentLockMode(b)==LockMode.NONE );
s.createQuery( "from Foo foo" ).list();
@ -1371,9 +1369,7 @@ public class FooBarTest extends LegacyTestCase {
assertTrue( s.getCurrentLockMode(b)==LockMode.READ);
s.evict(baz);
tx.commit();
s.disconnect();
s.reconnect();
tx = s.beginTransaction();
assertTrue( s.getCurrentLockMode(b)==LockMode.NONE );
s.delete( s.load( Baz.class, baz.getCode() ) );
@ -2575,12 +2571,9 @@ public class FooBarTest extends LegacyTestCase {
assertTrue( baz.getCascadingBars().size()==1 );
txn.commit();
s.disconnect();
s2 = (Session) SerializationHelper.deserialize( SerializationHelper.serialize(s) );
s.close();
s2.reconnect();
txn2 = s2.beginTransaction();
baz = (Baz) s2.load(Baz.class, baz.getCode());
assertTrue( ( (Long) s2.createQuery( "select count(*) from Bar" ).iterate().next() ).longValue()==3 );
@ -3951,35 +3944,6 @@ public class FooBarTest extends LegacyTestCase {
}
}
public void testDisconnect() throws Exception {
Session s = openSession();
s.beginTransaction();
Foo foo = new Foo();
Foo foo2 = new Foo();
s.save(foo);
s.save(foo2);
foo2.setFoo(foo);
s.getTransaction().commit();
s.disconnect();
s.reconnect();
s.beginTransaction();
s.delete(foo);
foo2.setFoo(null);
s.getTransaction().commit();
s.disconnect();
s.reconnect();
s.beginTransaction();
s.delete(foo2);
s.getTransaction().commit();
s.close();
}
public void testOrderBy() throws Exception {
Session s = openSession();
s.beginTransaction();
@ -4838,9 +4802,7 @@ public class FooBarTest extends LegacyTestCase {
t = s.beginTransaction();
Foo foo = (Foo) s.get(Foo.class, id);
t.commit();
s.disconnect();
s.reconnect();
t = s.beginTransaction();
s.flush();
t.commit();

View File

@ -735,6 +735,7 @@ public class FumTest extends LegacyTestCase {
// Test insertions across serializations
Session s = getSessions().openSession();
s.setFlushMode(FlushMode.MANUAL);
s.beginTransaction();
Simple simple = new Simple();
simple.setAddress("123 Main St. Anytown USA");
@ -745,11 +746,11 @@ public class FumTest extends LegacyTestCase {
s.save( simple, new Long(10) );
// Now, try to serialize session without flushing...
s.disconnect();
s.getTransaction().commit();
Session s2 = spoofSerialization(s);
s.close();
s = s2;
s.reconnect();
s.beginTransaction();
simple = (Simple) s.load( Simple.class, new Long(10) );
Simple other = new Simple();
@ -759,7 +760,7 @@ public class FumTest extends LegacyTestCase {
simple.setOther(other);
s.flush();
s.connection().commit();
s.getTransaction().commit();
s.close();
Simple check = simple;
@ -767,6 +768,7 @@ public class FumTest extends LegacyTestCase {
// Test updates across serializations
s = getSessions().openSession();
s.setFlushMode(FlushMode.MANUAL);
s.beginTransaction();
simple = (Simple) s.get( Simple.class, new Long(10) );
assertTrue("Not same parent instances", check.getName().equals( simple.getName() ) );
@ -774,14 +776,14 @@ public class FumTest extends LegacyTestCase {
simple.setName("My updated name");
s.disconnect();
s.getTransaction().commit();
s2 = spoofSerialization(s);
s.close();
s = s2;
s.reconnect();
s.beginTransaction();
s.flush();
s.connection().commit();
s.getTransaction().commit();
s.close();
check = simple;
@ -789,6 +791,7 @@ public class FumTest extends LegacyTestCase {
// Test deletions across serializations
s = getSessions().openSession();
s.setFlushMode(FlushMode.MANUAL);
s.beginTransaction();
simple = (Simple) s.get( Simple.class, new Long(10) );
assertTrue("Not same parent instances", check.getName().equals( simple.getName() ) );
@ -797,20 +800,21 @@ public class FumTest extends LegacyTestCase {
// Now, lets delete across serialization...
s.delete(simple);
s.disconnect();
s.getTransaction().commit();
s2 = spoofSerialization(s);
s.close();
s = s2;
s.reconnect();
s.beginTransaction();
s.flush();
s.connection().commit();
s.getTransaction().commit();
s.close();
///////////////////////////////////////////////////////////////////////////
// Test collection actions across serializations
s = getSessions().openSession();
s.setFlushMode(FlushMode.MANUAL);
s.beginTransaction();
Fum fum = new Fum( fumKey("uss-fum") );
fum.setFo( new Fum( fumKey("uss-fo") ) );
@ -828,32 +832,33 @@ public class FumTest extends LegacyTestCase {
s.save( fum.getFo() );
s.save(fum);
s.disconnect();
s.getTransaction().commit();
s2 = spoofSerialization(s);
s.close();
s = s2;
s.reconnect();
s.beginTransaction();
s.flush();
s.connection().commit();
s.getTransaction().commit();
s.close();
s = getSessions().openSession();
s.setFlushMode(FlushMode.MANUAL);
s.beginTransaction();
fum = (Fum) s.load( Fum.class, fum.getId() );
assertTrue("the Fum.friends did not get saved", fum.getFriends().size() == 2);
fum.setFriends(null);
s.disconnect();
s.getTransaction().commit();
s2 = spoofSerialization(s);
s.close();
s = s2;
s.reconnect();
s.beginTransaction();
s.flush();
s.connection().commit();
s.getTransaction().commit();
s.close();
s = getSessions().openSession();

View File

@ -181,7 +181,6 @@ public class ProxyTest extends FunctionalTestCase {
//close the original:
s.close();
sclone.reconnect();
t = sclone.beginTransaction();
DataPoint sdp = (DataPoint) sclone.load( DataPoint.class, new Long( dp.getId() ) );

View File

@ -11,6 +11,7 @@ import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.criterion.Order;
import org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.jta.platform.internal.JtaPlatformInitiator;
import org.hibernate.service.jta.platform.spi.JtaPlatform;
import org.hibernate.test.common.jta.AtomikosDataSourceConnectionProvider;
@ -20,6 +21,7 @@ import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.util.SerializationHelper;
import javax.transaction.Transaction;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -469,22 +471,6 @@ public class CMTTest extends FunctionalTestCase {
sfi().getServiceRegistry().getService( JtaPlatform.class ).retrieveTransactionManager().commit();
}
public void testAggressiveReleaseWithExplicitDisconnectReconnect() throws Exception {
sfi().getServiceRegistry().getService( JtaPlatform.class ).retrieveTransactionManager().begin();
Session s = getSessions().getCurrentSession();
s.createQuery( "from Item" ).list();
s.disconnect();
byte[] bytes = SerializationHelper.serialize( s );
s = ( Session ) SerializationHelper.deserialize( bytes );
s.reconnect();
s.createQuery( "from Item" ).list();
sfi().getServiceRegistry().getService( JtaPlatform.class ).retrieveTransactionManager().commit();
}
public void testAggressiveReleaseWithConnectionRetreival() throws Exception {
sfi().getServiceRegistry().getService( JtaPlatform.class ).retrieveTransactionManager().begin();
Session s = openSession();