From 2ddaeedd1ed06c39b43439f63cc66f1f42d3a1b3 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 28 Dec 2011 12:04:31 -0600 Subject: [PATCH] HHH-5275 - Criteria.setLockMode does not work correctly --- .../exception/LockAcquisitionException.java | 6 +- .../java/org/hibernate/test/locking/A.java | 67 +++++ .../hibernate/test/locking/LockModeTest.java | 271 ++++++++++++++++++ 3 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 hibernate-core/src/matrix/java/org/hibernate/test/locking/A.java create mode 100644 hibernate-core/src/matrix/java/org/hibernate/test/locking/LockModeTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/exception/LockAcquisitionException.java b/hibernate-core/src/main/java/org/hibernate/exception/LockAcquisitionException.java index 7ada8528a5..118d841f7f 100644 --- a/hibernate-core/src/main/java/org/hibernate/exception/LockAcquisitionException.java +++ b/hibernate-core/src/main/java/org/hibernate/exception/LockAcquisitionException.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * Copyright (c) 2008-2011, 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 Middleware LLC. + * 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 @@ -20,9 +20,9 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.exception; + import java.sql.SQLException; import org.hibernate.JDBCException; diff --git a/hibernate-core/src/matrix/java/org/hibernate/test/locking/A.java b/hibernate-core/src/matrix/java/org/hibernate/test/locking/A.java new file mode 100644 index 0000000000..84f00ddfd7 --- /dev/null +++ b/hibernate-core/src/matrix/java/org/hibernate/test/locking/A.java @@ -0,0 +1,67 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, 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.test.locking; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.annotations.GenericGenerator; + +/** + * @author Steve Ebersole + */ +@Entity +@Table( name = "T_LOCK_A" ) +public class A { + private Integer id; + private String value; + + public A() { + } + + public A(String value) { + this.value = value; + } + + @Id + @GeneratedValue( generator = "increment" ) + @GenericGenerator( name = "increment", strategy = "increment" ) + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/hibernate-core/src/matrix/java/org/hibernate/test/locking/LockModeTest.java b/hibernate-core/src/matrix/java/org/hibernate/test/locking/LockModeTest.java new file mode 100644 index 0000000000..3695fc2e39 --- /dev/null +++ b/hibernate-core/src/matrix/java/org/hibernate/test/locking/LockModeTest.java @@ -0,0 +1,271 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, 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.test.locking; + +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.PessimisticLockException; +import org.hibernate.Session; +import org.hibernate.exception.GenericJDBCException; +import org.hibernate.exception.LockAcquisitionException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +/** + * Make sure that directly specifying lock modes, even though deprecated, continues to work until removed. + * + * @author Steve Ebersole + */ +@TestForIssue( jiraKey = "HHH-5275") +public class LockModeTest extends BaseCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { A.class }; + } + + @Before + public void createData() { + Session session = sessionFactory().openSession(); + session.beginTransaction(); + session.save( new A( "it" ) ); + session.getTransaction().commit(); + session.close(); + } + + @After + public void cleanupData() { + Session session = sessionFactory().openSession(); + session.beginTransaction(); + session.createQuery( "delete A" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @Test + @SuppressWarnings( {"deprecation"}) + public void testLoading() { + // open a session, begin a transaction and lock row + Session s1 = sessionFactory().openSession(); + s1.beginTransaction(); + try { + A it = (A) s1.get( A.class, 1, LockMode.PESSIMISTIC_WRITE ); + // make sure we got it + assertNotNull( it ); + + // that initial transaction is still active and so the lock should still be held. + // Lets open another session/transaction and verify + Session s2 = sessionFactory().openSession(); + s2.beginTransaction(); + try { + s2.get( A.class, 1, LockMode.PESSIMISTIC_WRITE ); + fail( "Pessimistic lock was not obtained/held" ); + } + catch ( Exception e ) { + // grr, exception can be any number of types based on database + // see HHH-6887 + if ( LockAcquisitionException.class.isInstance( e ) + || GenericJDBCException.class.isInstance( e ) + || PessimisticLockException.class.isInstance( e ) ) { + // "ok" + } + else { + fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() ); + } + } + finally { + s2.getTransaction().commit(); + s2.close(); + } + } + finally { + s1.getTransaction().commit(); + s1.close(); + } + } + + @Test + @FailureExpected( jiraKey = "HHH-5275" ) + public void testLegacyCriteria() { + // open a session, begin a transaction and lock row + Session s1 = sessionFactory().openSession(); + s1.beginTransaction(); + try { + A it = (A) s1.createCriteria( A.class ) + .setLockMode( LockMode.PESSIMISTIC_WRITE ) + .uniqueResult(); + // make sure we got it + assertNotNull( it ); + + // that initial transaction is still active and so the lock should still be held. + // Lets open another session/transaction and verify + Session s2 = sessionFactory().openSession(); + s2.beginTransaction(); + try { + s2.createCriteria( A.class ) + .setLockMode( LockMode.PESSIMISTIC_WRITE ) + .uniqueResult(); + + fail( "Pessimistic lock was not obtained/held" ); + } + catch ( Exception e ) { + // grr, exception can be any number of types based on database + // see HHH-6887 + if ( LockAcquisitionException.class.isInstance( e ) + || GenericJDBCException.class.isInstance( e ) + || PessimisticLockException.class.isInstance( e ) ) { + // "ok" + } + else { + fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() ); + } + } + finally { + s2.getTransaction().commit(); + s2.close(); + } + } + finally { + s1.getTransaction().commit(); + s1.close(); + } + } + + @Test + @FailureExpected( jiraKey = "HHH-5275" ) + public void testLegacyCriteriaAliasSpecific() { + // open a session, begin a transaction and lock row + Session s1 = sessionFactory().openSession(); + s1.beginTransaction(); + try { + A it = (A) s1.createCriteria( A.class ) + .setLockMode( "this", LockMode.PESSIMISTIC_WRITE ) + .uniqueResult(); + // make sure we got it + assertNotNull( it ); + + // that initial transaction is still active and so the lock should still be held. + // Lets open another session/transaction and verify + Session s2 = sessionFactory().openSession(); + s2.beginTransaction(); + try { + s2.createCriteria( A.class ) + .setLockMode( "this", LockMode.PESSIMISTIC_WRITE ) + .uniqueResult(); + + fail( "Pessimistic lock was not obtained/held" ); + } + catch ( Exception e ) { + // grr, exception can be any number of types based on database + // see HHH-6887 + if ( LockAcquisitionException.class.isInstance( e ) + || GenericJDBCException.class.isInstance( e ) + || PessimisticLockException.class.isInstance( e ) ) { + // "ok" + } + else { + fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() ); + } + } + finally { + s2.getTransaction().commit(); + s2.close(); + } + } + finally { + s1.getTransaction().commit(); + s1.close(); + } + } + + @Test + @FailureExpected( jiraKey = "HHH-5275" ) + public void testQuery() { + // open a session, begin a transaction and lock row + Session s1 = sessionFactory().openSession(); + s1.beginTransaction(); + try { + A it = (A) s1.createQuery( "from A a" ) + .setLockMode( "a", LockMode.PESSIMISTIC_WRITE ) + .uniqueResult(); + // make sure we got it + assertNotNull( it ); + + // that initial transaction is still active and so the lock should still be held. + // Lets open another session/transaction and verify + Session s2 = sessionFactory().openSession(); + s2.beginTransaction(); + try { + s2.createQuery( "from A a" ) + .setLockMode( "a", LockMode.PESSIMISTIC_WRITE ) + .uniqueResult(); + + fail( "Pessimistic lock was not obtained/held" ); + } + catch ( Exception e ) { + // grr, exception can be any number of types based on database + // see HHH-6887 + if ( LockAcquisitionException.class.isInstance( e ) + || GenericJDBCException.class.isInstance( e ) + || PessimisticLockException.class.isInstance( e ) ) { + // "ok" + } + else { + fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() ); + } + } + finally { + s2.getTransaction().commit(); + s2.close(); + } + } + finally { + s1.getTransaction().commit(); + s1.close(); + } + } + + @Test + public void testQueryUsingLockOptions() { + // todo : need an association here to make sure the alias-specific lock modes are applied correctly + Session s1 = sessionFactory().openSession(); + s1.beginTransaction(); + s1.createQuery( "from A a" ) + .setLockOptions( new LockOptions( LockMode.PESSIMISTIC_WRITE ) ) + .uniqueResult(); + s1.createQuery( "from A a" ) + .setLockOptions( new LockOptions().setAliasSpecificLockMode( "a", LockMode.PESSIMISTIC_WRITE ) ) + .uniqueResult(); + s1.getTransaction().commit(); + s1.close(); + } +}