From cab613de11deb3c3fabc43211908fe97d1fd248b Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 15 May 2017 14:38:22 -0700 Subject: [PATCH] HHH-11740 - Fix test case to avoid pessimistic locking exception --- .../test/unionsubclass/UnionSubclassTest.java | 137 ++++++++++++------ 1 file changed, 92 insertions(+), 45 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java b/hibernate-core/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java index 85267c9e44..90bf99b6c1 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java @@ -6,6 +6,7 @@ */ package org.hibernate.test.unionsubclass; +import java.sql.Connection; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -15,12 +16,16 @@ import org.hibernate.FetchMode; import org.hibernate.Hibernate; import org.hibernate.Query; import org.hibernate.Session; +import org.hibernate.SharedSessionContract; import org.hibernate.Transaction; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.criterion.Order; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.jdbc.Work; +import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; @@ -33,6 +38,7 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -437,67 +443,108 @@ public class UnionSubclassTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-11740" ) - public void testBulkOperationsInTwoConcurrentSessions() throws Exception { - doInHibernate( this::sessionFactory, s -> { - Location mars = new Location( "Mars" ); - s.persist( mars ); + public void testBulkOperationsWithDifferentConnections() throws Exception { + doInHibernate( + this::sessionFactory, s -> { + Location mars = new Location( "Mars" ); + s.persist( mars ); - Location earth = new Location( "Earth" ); - s.persist( earth ); + Location earth = new Location( "Earth" ); + s.persist( earth ); - Hive hive = new Hive(); - hive.setLocation( mars ); - s.persist( hive ); + Hive hive = new Hive(); + hive.setLocation( mars ); + s.persist( hive ); - Alien alien = new Alien(); - alien.setIdentity( "Uncle Martin" ); - alien.setSpecies( "Martian" ); - alien.setHive( hive ); - hive.getMembers().add( alien ); - mars.addBeing( alien ); + Alien alien = new Alien(); + alien.setIdentity( "Uncle Martin" ); + alien.setSpecies( "Martian" ); + alien.setHive( hive ); + hive.getMembers().add( alien ); + mars.addBeing( alien ); - s.persist( alien ); + s.persist( alien ); - Human human = new Human(); - human.setIdentity( "Jane Doe" ); - human.setSex( 'M' ); - earth.addBeing( human ); + Human human = new Human(); + human.setIdentity( "Jane Doe" ); + human.setSex( 'M' ); + earth.addBeing( human ); - s.persist( human ); - } ); + s.persist( human ); + } + ); - AtomicBoolean pessimisticLocking = new AtomicBoolean( false ); + // The following tests that bulk operations can be executed using 2 different + // connections. doInHibernate( this::sessionFactory, s1 -> { - TransactionUtil.setJdbcTimeout( s1 ); + // Transaction used by s1 is already started. + // Assert that the Connection is already physically connected. + SharedSessionContractImplementor s1Implementor = (SharedSessionContractImplementor) s1; + assertTrue( s1Implementor.getJdbcCoordinator().getLogicalConnection().isPhysicallyConnected() ); + + // Assert that the same Connection will be used for s1's entire transaction + assertEquals( + PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION, + s1Implementor.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode() + ); + + // Get the Connection s1 will use. + final Connection connection1 = s1Implementor.connection(); + + // Avoid a pessimistic lock exception by not doing anything with s1 until + // after a second Session (with a different connection) is used + // for a bulk operation. + + doInHibernate( this::sessionFactory, s2 -> { + // Check same assertions for s2 as was done for s1. + SharedSessionContractImplementor s2Implementor = (SharedSessionContractImplementor) s2; + assertTrue( s2Implementor.getJdbcCoordinator().getLogicalConnection().isPhysicallyConnected() ); + assertEquals( + PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION, + s2Implementor.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode() + ); + + // Get the Connection s2 will use. + Connection connection2 = s2Implementor.connection(); + + // Assert that connection2 is not the same as connection1 + assertNotSame( connection1, connection2 ); + + // Execute a bulk operation on s2 (using connection2) + assertEquals( + 1, + s2.createQuery( "delete from Being where species = 'Martian'" ).executeUpdate() + ); + + // Assert the Connection has not changed + assertSame( connection2, s2Implementor.connection() ); + } + ); + + // Assert that the Connection used by s1 has hot changed. + assertSame( connection1, s1Implementor.connection() ); + + // Execute a bulk operation on s1 (using connection1) assertEquals( 1, s1.createQuery( "update Being set identity = 'John Doe' where identity = 'Jane Doe'" ) .executeUpdate() ); - try { - doInHibernate( this::sessionFactory, s2 -> { - TransactionUtil.setJdbcTimeout( s2 ); - assertEquals( 1, s2.createQuery( "delete from Being where species = 'Martian'" ).executeUpdate() ); - } ); - } - catch (Exception e) { - if ( !ExceptionUtil.isSqlLockTimeout(e) ) { - fail( e.getMessage() ); - } - pessimisticLocking.set( true ); - } - } ); + // Assert that the Connection used by s1 has hot changed. + assertSame( connection1, s1Implementor.connection() ); - doInHibernate( this::sessionFactory, s -> { - if ( !pessimisticLocking.get() ) { - Human human = (Human) s.createQuery( "from Being" ).uniqueResult(); - assertEquals( "John Doe", human.getIdentity() ); - } - s.createQuery( "delete from Being" ).executeUpdate(); - s.createQuery( "delete from Hive" ).executeUpdate(); - s.createQuery( "delete from Location" ).executeUpdate(); + }); + + // Clean up + doInHibernate( + this::sessionFactory, s -> { + Human human = (Human) s.createQuery( "from Being" ).uniqueResult(); + assertEquals( "John Doe", human.getIdentity() ); + s.createQuery( "delete from Being" ).executeUpdate(); + s.createQuery( "delete from Hive" ).executeUpdate(); + s.createQuery( "delete from Location" ).executeUpdate(); } ); } }