From 5fd186a01057699a6220061aa6dcef688b91e471 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 3 Apr 2017 20:27:03 +0100 Subject: [PATCH] HHH-10654 - Fix mariadb and pgsql failing tests --- .../org/hibernate/jpa/test/lock/LockTest.java | 20 +++-- .../SearchedCaseExpressionTest.java | 11 ++- .../hibernate/test/locking/LockModeTest.java | 67 +++++++-------- .../testing/junit4/BaseUnitTestCase.java | 2 +- .../testing/transaction/TransactionUtil.java | 45 ++++++++++ .../hibernate/testing/util/ExceptionUtil.java | 85 +++++++++++++++++++ 6 files changed, 186 insertions(+), 44 deletions(-) create mode 100644 hibernate-testing/src/main/java/org/hibernate/testing/util/ExceptionUtil.java diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java index 64dab47d0b..1134564b79 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java @@ -23,6 +23,7 @@ import javax.persistence.PessimisticLockException; import javax.persistence.Query; import javax.persistence.QueryTimeoutException; +import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.HSQLDialect; @@ -40,6 +41,8 @@ import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.transaction.TransactionUtil; +import org.hibernate.testing.util.ExceptionUtil; import org.junit.Test; import org.jboss.logging.Logger; @@ -156,19 +159,23 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { Map properties = new HashMap<>(); - properties.put( org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT, -2L ); + properties.put( org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT, LockOptions.SKIP_LOCKED ); em2.find( Lock.class, lock.getId(), LockModeType.PESSIMISTIC_READ, properties ); try { doInJPA( this::entityManagerFactory, entityManager -> { + TransactionUtil.setJdbcTimeout( entityManager.unwrap( Session.class ) ); entityManager.createNativeQuery( updateStatement() ) .setParameter( "name", "changed" ) .setParameter( "id", lock.getId() ) .executeUpdate(); } ); - fail("Should throw LockTimeoutException"); + fail("Should throw Exception"); } - catch (LockTimeoutException expected) { + catch (Exception e) { + if ( !ExceptionUtil.isSqlLockTimeout( e) ) { + fail( "Unknown exception thrown: " + e.getMessage() ); + } } } finally { @@ -202,13 +209,16 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { doInJPA( this::entityManagerFactory, entityManager -> { try { + TransactionUtil.setJdbcTimeout( entityManager.unwrap( Session.class ) ); entityManager.createNativeQuery( updateStatement() ) .setParameter( "name", "changed" ) .setParameter( "id", lock.getId() ) .executeUpdate(); } - catch (LockTimeoutException | PessimisticLockException expected) { - failureExpected.set( true ); + catch (Exception e) { + if ( ExceptionUtil.isSqlLockTimeout( e ) ) { + failureExpected.set( true ); + } } } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/expression/SearchedCaseExpressionTest.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/expression/SearchedCaseExpressionTest.java index b89bfacbd7..29a055efa4 100644 --- a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/expression/SearchedCaseExpressionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/expression/SearchedCaseExpressionTest.java @@ -9,6 +9,8 @@ package org.hibernate.query.criteria.internal.expression; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; @@ -16,6 +18,11 @@ import javax.persistence.criteria.Expression; import javax.persistence.criteria.Path; import javax.persistence.criteria.Root; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.dialect.PostgreSQL81Dialect; + +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Assert; import org.junit.Test; @@ -29,7 +36,8 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; public class SearchedCaseExpressionTest extends BaseCoreFunctionalTestCase { @Test - public void testCaseClause() { + @RequiresDialect(H2Dialect.class) + public void testCaseClause() { doInHibernate( this::sessionFactory, session -> { CriteriaBuilder cb = session.getCriteriaBuilder(); @@ -87,6 +95,7 @@ public class SearchedCaseExpressionTest extends BaseCoreFunctionalTestCase { private Long id; @Column + @Enumerated(EnumType.STRING) private EventType type; protected Event() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java b/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java index eb3383d87d..f008148b13 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java @@ -7,19 +7,17 @@ package org.hibernate.test.locking; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.PessimisticLockException; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.SybaseASE15Dialect; -import org.hibernate.exception.GenericJDBCException; -import org.hibernate.exception.LockAcquisitionException; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.transaction.TransactionUtil; +import org.hibernate.testing.util.ExceptionUtil; import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; @@ -164,43 +162,38 @@ public class LockModeTest extends BaseCoreFunctionalTestCase { // until the txn in the calling method completed. // To be able to cater to the second type, we run this block in a separate thread to be able to "time it out" - executeSync( () -> { - doInHibernate( this::sessionFactory, _session -> { - _session.doWork( connection -> { + try { + executeSync( () -> { + doInHibernate( this::sessionFactory, _session -> { + TransactionUtil.setJdbcTimeout( _session ); try { - connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), 1000); - } catch (Throwable ignore) { - ignore.fillInStackTrace(); + // load with write lock to deal with databases that block (wait indefinitely) direct attempts + // to write a locked row + A it = _session.get( + A.class, + id, + new LockOptions( LockMode.PESSIMISTIC_WRITE ).setTimeOut( LockOptions.NO_WAIT ) + ); + _session.createNativeQuery( updateStatement() ) + .setParameter( "value", "changed" ) + .setParameter( "id", it.getId() ) + .executeUpdate(); + fail( "Pessimistic lock not obtained/held" ); + } + catch ( Exception e ) { + if ( !ExceptionUtil.isSqlLockTimeout( e) ) { + fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() ); + } } } ); - try { - // load with write lock to deal with databases that block (wait indefinitely) direct attempts - // to write a locked row - A it = _session.get( - A.class, - id, - new LockOptions( LockMode.PESSIMISTIC_WRITE ).setTimeOut( LockOptions.NO_WAIT ) - ); - _session.createNativeQuery( updateStatement() ) - .setParameter( "value", "changed" ) - .setParameter( "id", it.getId() ) - .executeUpdate(); - fail( "Pessimistic lock 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() ); - } - } } ); - } ); + } + catch (Exception e) { + //MariaDB throws a time out nd closes the underlying connection + if( !ExceptionUtil.isConnectionClose(e)) { + fail("Unknown exception thrown: " + e.getMessage()); + } + } } protected String updateStatement() { diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseUnitTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseUnitTestCase.java index b3ca7e6dc7..ae79ff24b4 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseUnitTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseUnitTestCase.java @@ -91,7 +91,7 @@ public abstract class BaseUnitTestCase { Thread.currentThread().interrupt(); } catch (ExecutionException e) { - throw new RuntimeException( e ); + throw new RuntimeException( e.getCause() ); } } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java index efa3e424c7..3c86be1008 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java @@ -6,6 +6,8 @@ */ package org.hibernate.testing.transaction; +import java.sql.Statement; +import java.util.concurrent.Executors; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -13,16 +15,25 @@ import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; +import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionBuilder; import org.hibernate.SessionFactory; import org.hibernate.Transaction; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.PostgreSQL81Dialect; + +import org.jboss.logging.Logger; /** * @author Vlad Mihalcea */ public class TransactionUtil { + private static final Logger log = Logger.getLogger( TransactionUtil.class ); + /** * Hibernate transaction function * @@ -322,4 +333,38 @@ public class TransactionUtil { } } } + + /** + * Set Session or Statement timeout + * @param session Hibernate Session + */ + public static void setJdbcTimeout(Session session) { + session.doWork( connection -> { + if ( Dialect.getDialect() instanceof PostgreSQL81Dialect ) { + try (Statement st = connection.createStatement()) { + st.execute( "SET statement_timeout TO 1000" ); + } + + } + else if( Dialect.getDialect() instanceof MySQLDialect ) { + try (Statement st = connection.createStatement()) { + st.execute( "SET GLOBAL innodb_lock_wait_timeout = 1" ); + } + } + else if( Dialect.getDialect() instanceof H2Dialect ) { + try (Statement st = connection.createStatement()) { + st.execute( "SET LOCK_TIMEOUT 100" ); + } + } + else { + try { + connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), 1000 ); + } + catch (Throwable ignore) { + ignore.fillInStackTrace(); + } + } + } ); + } + } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/util/ExceptionUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/util/ExceptionUtil.java new file mode 100644 index 0000000000..19f84f2405 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/util/ExceptionUtil.java @@ -0,0 +1,85 @@ +package org.hibernate.testing.util; + +import javax.persistence.LockTimeoutException; + +import org.hibernate.PessimisticLockException; +import org.hibernate.exception.GenericJDBCException; +import org.hibernate.exception.JDBCConnectionException; +import org.hibernate.exception.LockAcquisitionException; + +/** + * @author Vlad Mihalcea + */ +public class ExceptionUtil { + + private static final ExceptionUtil INSTANCE = new ExceptionUtil(); + + public static ExceptionUtil getInstance() { + return INSTANCE; + } + + private ExceptionUtil() { + } + + /** + * Get the root cause of a particular {@code Throwable} + * + * @param t exception + * + * @return exception root cause + */ + public static Throwable rootCause(Throwable t) { + Throwable cause = t.getCause(); + if ( cause != null && cause != t ) { + return rootCause( cause ); + } + return t; + } + + /** + * Was the given exception caused by a SQL lock timeout? + * + * @param e exception + * + * @return is caused by a SQL lock timeout + */ + public static boolean isSqlLockTimeout(Exception e) { + // grr, exception can be any number of types based on database + // see HHH-6887 + if ( LockAcquisitionException.class.isInstance( e ) + || LockTimeoutException.class.isInstance( e ) + || GenericJDBCException.class.isInstance( e ) + || PessimisticLockException.class.isInstance( e ) + || javax.persistence.PessimisticLockException.class.isInstance( e ) + || JDBCConnectionException.class.isInstance( e ) ) { + return true; + } + else { + Throwable rootCause = ExceptionUtil.rootCause( e ); + if ( rootCause != null && ( + rootCause.getMessage().contains( "timeout" ) || + rootCause.getMessage().contains( "timed out" ) ) + ) { + return true; + } + } + return false; + } + + /** + * Was the given exception caused by a SQL connection close + * + * @param e exception + * + * @return is caused by a SQL connection close + */ + public static boolean isConnectionClose(Exception e) { + Throwable rootCause = ExceptionUtil.rootCause( e ); + if ( rootCause != null && ( + rootCause.getMessage().toLowerCase().contains( "connection is close" ) + ) ) { + return true; + } + return false; + } +}