HHH-12559 - Add support for MySQL 8 SKIP LOCKED and NOWAIT

This commit is contained in:
Vlad Mihalcea 2018-05-10 14:04:12 +03:00
parent 2d55060ff6
commit 5cfb0a508c
25 changed files with 250 additions and 40 deletions

View File

@ -56,7 +56,7 @@ ext {
'jdbc.driver': 'com.mysql.jdbc.Driver',
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
'jdbc.url' : 'jdbc:mysql://127.0.0.1/hibernate_orm_test'
'jdbc.url' : 'jdbc:mysql://127.0.0.1/hibernate_orm_test?useSSL=false'
],
mariadb : [
'db.dialect' : '',

View File

@ -103,7 +103,8 @@ ext {
hsqldb: "org.hsqldb:hsqldb:2.3.2",
derby: "org.apache.derby:derby:10.11.1.1",
postgresql: 'org.postgresql:postgresql:42.2.2',
mysql: 'mysql:mysql-connector-java:6.0.5',
//Upgrade MySQL Driver only when this issue gets fixed: https://bugs.mysql.com/bug.php?id=85941
mysql: 'mysql:mysql-connector-java:5.1.46',
mariadb: 'org.mariadb.jdbc:mariadb-java-client:2.2.3',
oracle: 'com.oracle.jdbc:ojdbc7:12.1.0.2',

View File

@ -1604,4 +1604,9 @@ public abstract class AbstractHANADialect extends Dialect {
return 0;
}
@Override
public boolean supportsNoWait() {
return true;
}
}

View File

@ -2935,6 +2935,15 @@ public abstract class Dialect implements ConversionContext {
return false;
}
/**
* Does this dialect/database support NO_WAIT timeout.
*
* @return {@code true} if NO_WAIT is supported
*/
public boolean supportsNoWait() {
return false;
}
public boolean isLegacyLimitHandlerBehaviorEnabled() {
return legacyLimitHandlerBehavior;
}

View File

@ -0,0 +1,95 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.dialect;
import org.hibernate.LockOptions;
/**
* @author Vlad Mihalcea
*/
public class MySQL8Dialect extends MySQL57Dialect {
@Override
public String getWriteLockString(int timeout) {
if ( timeout == LockOptions.NO_WAIT ) {
return getForUpdateNowaitString();
}
else if ( timeout == LockOptions.SKIP_LOCKED ) {
return getForUpdateSkipLockedString();
}
return super.getWriteLockString( timeout );
}
@Override
public String getWriteLockString(String aliases, int timeout) {
if ( timeout == LockOptions.NO_WAIT ) {
return getForUpdateNowaitString(aliases);
}
else if ( timeout == LockOptions.SKIP_LOCKED ) {
return getForUpdateSkipLockedString(aliases);
}
return super.getWriteLockString( aliases, timeout );
}
@Override
public String getReadLockString(int timeout) {
String readLockString = " for share";
if ( timeout == LockOptions.NO_WAIT ) {
return readLockString + " nowait ";
}
else if ( timeout == LockOptions.SKIP_LOCKED ) {
return readLockString + " skip locked ";
}
return readLockString;
}
@Override
public String getReadLockString(String aliases, int timeout) {
String readLockString = String.format( " for share of %s ", aliases );
if ( timeout == LockOptions.NO_WAIT ) {
return readLockString + " nowait ";
}
else if ( timeout == LockOptions.SKIP_LOCKED ) {
return readLockString + " skip locked ";
}
return readLockString;
}
@Override
public String getForUpdateSkipLockedString() {
return " for update skip locked";
}
@Override
public String getForUpdateSkipLockedString(String aliases) {
return getForUpdateString() + " of " + aliases + " skip locked";
}
@Override
public String getForUpdateNowaitString() {
return getForUpdateString() + " nowait ";
}
@Override
public String getForUpdateNowaitString(String aliases) {
return getForUpdateString( aliases ) + " nowait ";
}
@Override
public String getForUpdateString(String aliases) {
return getForUpdateString() + " of " + aliases;
}
@Override
public boolean supportsSkipLocked() {
return true;
}
public boolean supportsNoWait() {
return true;
}
}

View File

@ -761,4 +761,9 @@ public class Oracle8iDialect extends Dialect {
throw new IllegalArgumentException( "Can't determine SQL statement type for statement: " + sql );
}
@Override
public boolean supportsNoWait() {
return true;
}
}

View File

@ -401,4 +401,9 @@ public class Oracle9Dialect extends Dialect {
public boolean canCreateSchema() {
return false;
}
@Override
public boolean supportsNoWait() {
return true;
}
}

View File

@ -632,4 +632,9 @@ public class PostgreSQL81Dialect extends Dialect {
public boolean supportsNationalizedTypes() {
return false;
}
@Override
public boolean supportsNoWait() {
return true;
}
}

View File

@ -122,4 +122,9 @@ public class SQLServer2005Dialect extends SQLServerDialect {
public boolean supportsSkipLocked() {
return true;
}
@Override
public boolean supportsNoWait() {
return true;
}
}

View File

@ -23,6 +23,10 @@ import org.hibernate.StaleStateException;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQL57Dialect;
import org.hibernate.dialect.MySQL8Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.Oracle10gDialect;
import org.hibernate.query.Query;
import org.hibernate.tool.hbm2ddl.SchemaExport;
@ -369,14 +373,15 @@ public class EntityTest extends BaseNonConfigCoreFunctionalTestCase {
@SkipForDialect(value = Oracle10gDialect.class, comment = "oracle12c returns time in getDate. For now, skip.")
public void testTemporalType() throws Exception {
final ZoneId zoneId = ( Dialect.getDialect() instanceof MySQL8Dialect ) ? ZoneId.of( "UTC")
: ZoneId.systemDefault();
Flight airFrance = doInHibernate( this::sessionFactory, session -> {
Flight _airFrance = new Flight();
_airFrance.setId( Long.valueOf( 747 ) );
_airFrance.setName( "Paris-Amsterdam" );
_airFrance.setDuration( Long.valueOf( 10 ) );
_airFrance.setDepartureDate( Date.from(LocalDate.of( 2005, 06, 21 ).atStartOfDay(
ZoneId.systemDefault()).toInstant()) );
_airFrance.setDepartureDate( Date.from(LocalDate.of( 2005, 06, 21 ).atStartOfDay(zoneId).toInstant()) );
_airFrance.setAlternativeDepartureDate( new GregorianCalendar( 2006, 02, 03, 10, 00 ) );
_airFrance.getAlternativeDepartureDate().setTimeZone( TimeZone.getTimeZone( "GMT" ) );
_airFrance.setBuyDate( new java.sql.Timestamp( 122367443 ) );
@ -392,8 +397,7 @@ public class EntityTest extends BaseNonConfigCoreFunctionalTestCase {
Flight copyAirFrance = (Flight) q.uniqueResult();
assertNotNull( copyAirFrance );
assertEquals(
Date.from(LocalDate.of( 2005, 06, 21 ).atStartOfDay(
ZoneId.systemDefault()).toInstant()),
Date.from(LocalDate.of( 2005, 06, 21 ).atStartOfDay(zoneId).toInstant()),
copyAirFrance.getDepartureDate()
);
assertEquals( df.format( airFrance.getBuyDate() ), df.format( copyAirFrance.getBuyDate() ) );

View File

@ -15,7 +15,7 @@
<hibernate-mapping package="org.hibernate.test.extralazy">
<class name="Group" table="groups">
<class name="Group" table="groups_">
<id name="name"/>
<map name="users" cascade="persist"
table="group_user" lazy="extra">

View File

@ -14,20 +14,28 @@ import javax.persistence.PessimisticLockException;
import org.hibernate.LockOptions;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.SkipForDialects;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jdbc.SQLServerSnapshotIsolationConnectionProvider;
import org.hibernate.testing.transaction.TransactionUtil2;
import org.hibernate.testing.util.ExceptionUtil;
import org.hibernate.test.jpa.AbstractJPATest;
import org.hibernate.test.jpa.Item;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Steve Ebersole
*/
@RequiresDialectFeature(DialectChecks.SupportNoWait.class)
public class LockExceptionTests extends AbstractJPATest {
@Override
public void configure(Configuration cfg) {
@ -72,7 +80,7 @@ public class LockExceptionTests extends AbstractJPATest {
);
}
catch (Exception e) {
e.printStackTrace();
log.error( "Exception thrown", e );
}
finally {
inTransaction(

View File

@ -9,6 +9,8 @@ import javax.persistence.Id;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.dialect.MySQL57Dialect;
import org.hibernate.dialect.MySQL8Dialect;
import org.hibernate.dialect.Oracle8iDialect;
import org.hibernate.dialect.PostgreSQL95Dialect;
import org.hibernate.dialect.SQLServer2005Dialect;
@ -141,6 +143,45 @@ public abstract class AbstractSkipLockedTest
} );
}
@Test
@RequiresDialect({ MySQL8Dialect.class })
public void testMySQLSkipLocked() {
doInHibernate( this::sessionFactory, session -> {
for ( long i = 1; i <= 10; i++ ) {
BatchJob batchJob = new BatchJob();
batchJob.setId( i );
session.persist( batchJob );
}
} );
doInHibernate( this::sessionFactory, session -> {
List<BatchJob> firstFive = nextFiveBatchJobs( session );
assertEquals( 5, firstFive.size() );
assertTrue( firstFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
.containsAll( Arrays.asList( 1L, 2L, 3L, 4L, 5L ) ) );
executeSync( () -> {
doInHibernate( this::sessionFactory, _session -> {
List<BatchJob> nextFive = nextFiveBatchJobs( _session );
assertEquals( 5, nextFive.size() );
if ( lockMode() == LockMode.PESSIMISTIC_READ ) {
assertTrue( nextFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
.containsAll( Arrays.asList( 1L, 2L, 3L, 4L, 5L ) ) );
}
else {
assertTrue( nextFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
.containsAll( Arrays.asList( 6L, 7L, 8L, 9L, 10L ) ) );
}
} );
} );
} );
}
private List<BatchJob> nextFiveBatchJobs(Session session) {
return nextFiveBatchJobs( session, 5 );
}

View File

@ -15,7 +15,7 @@
<hibernate-mapping package="org.hibernate.test.map">
<class name="Group" table="groups">
<class name="Group" table="groups_">
<id name="name"/>
<map name="users" cascade="persist" table="group_user">
<key column="groupName"/>

View File

@ -15,7 +15,7 @@
<hibernate-mapping package="org.hibernate.test.mapelemformula">
<class name="Group" table="groups">
<class name="Group" table="groups_">
<id name="name"/>
<map name="users" cascade="persist" table="group_user">
<key column="groupName"/>

View File

@ -39,7 +39,7 @@
</set>
<bag name="systems" table="USER_SYSTEM" lazy="true" inverse="false" cascade="all">
<key column="USER_ID" property-ref="userId" />
<element type="string" column="SYSTEM" />
<element type="string" column="SYSTEM_ID" />
</bag>
</class>

View File

@ -11,6 +11,7 @@ import javax.sql.DataSource;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQL8Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.testing.RequiresDialect;
@ -25,10 +26,16 @@ public class MySQLSkipAutoCommitTest extends AbstractSkipAutoCommitTest {
@Override
protected DataSource dataSource() {
DataSource dataSource = ReflectionUtil.newInstance( "com.mysql.cj.jdbc.MysqlDataSource" );
DataSource dataSource = null;
if ( getDialect() instanceof MariaDBDialect ) {
dataSource = ReflectionUtil.newInstance( "org.mariadb.jdbc.MariaDbDataSource" );
}
else if ( getDialect() instanceof MySQL8Dialect ) {
dataSource = ReflectionUtil.newInstance( "com.mysql.cj.jdbc.MysqlDataSource" );
}
else if ( getDialect() instanceof MySQLDialect ) {
dataSource = ReflectionUtil.newInstance( "com.mysql.jdbc.jdbc2.optional.MysqlDataSource" );
}
ReflectionUtil.setProperty( dataSource, "url", Environment.getProperties().getProperty( AvailableSettings.URL ) );
ReflectionUtil.setProperty( dataSource, "user", Environment.getProperties().getProperty( AvailableSettings.USER ) );
ReflectionUtil.setProperty( dataSource, "password", Environment.getProperties().getProperty( AvailableSettings.PASS ) );

View File

@ -60,7 +60,7 @@ public class MySQL57TimestampPropertyTest extends BaseCoreFunctionalTestCase {
s = openSession();
s.getTransaction().begin();
final Query queryWithParameter = s.createQuery( "from MySQL57TimestampPropertyTest$Entity where ts=?" ).setParameter( 0, eOrig.ts );
final Query queryWithParameter = s.createQuery( "from Entity where ts= ?1" ).setParameter( 1, eOrig.ts );
final Entity eQueriedWithParameter = (Entity) queryWithParameter.uniqueResult();
assertNotNull( eQueriedWithParameter );
s.getTransaction().commit();
@ -68,7 +68,7 @@ public class MySQL57TimestampPropertyTest extends BaseCoreFunctionalTestCase {
s = openSession();
s.getTransaction().begin();
final Query queryWithTimestamp = s.createQuery( "from MySQL57TimestampPropertyTest$Entity where ts=?" ).setTimestamp( 0, eOrig.ts );
final Query queryWithTimestamp = s.createQuery( "from Entity where ts= ?1" ).setTimestamp( 1, eOrig.ts );
final Entity eQueriedWithTimestamp = (Entity) queryWithTimestamp.uniqueResult();
assertNotNull( eQueriedWithTimestamp );
s.getTransaction().commit();
@ -105,8 +105,8 @@ public class MySQL57TimestampPropertyTest extends BaseCoreFunctionalTestCase {
s = openSession();
s.getTransaction().begin();
final Query queryWithParameter =
s.createQuery( "from MySQL57TimestampPropertyTest$Entity where tsColumnDefault=?" )
.setParameter( 0, eOrig.tsColumnDefault );
s.createQuery( "from Entity where tsColumnDefault= ?1" )
.setParameter( 1, eOrig.tsColumnDefault );
final Entity eQueriedWithParameter = (Entity) queryWithParameter.uniqueResult();
assertNotNull( eQueriedWithParameter );
s.getTransaction().commit();
@ -115,8 +115,8 @@ public class MySQL57TimestampPropertyTest extends BaseCoreFunctionalTestCase {
s = openSession();
s.getTransaction().begin();
final Query queryWithTimestamp =
s.createQuery( "from MySQL57TimestampPropertyTest$Entity where tsColumnDefault=?" )
.setTimestamp( 0, eOrig.tsColumnDefault );
s.createQuery( "from Entity where tsColumnDefault= ?1" )
.setTimestamp( 1, eOrig.tsColumnDefault );
final Entity eQueriedWithTimestamp = (Entity) queryWithTimestamp.uniqueResult();
assertNotNull( eQueriedWithTimestamp );
s.getTransaction().commit();
@ -153,8 +153,8 @@ public class MySQL57TimestampPropertyTest extends BaseCoreFunctionalTestCase {
s = openSession();
s.getTransaction().begin();
final Query queryWithParameter =
s.createQuery( "from MySQL57TimestampPropertyTest$Entity where tsColumnDefinition=?" )
.setParameter( 0, eOrig.tsColumnDefinition );
s.createQuery( "from Entity where tsColumnDefinition= ?1" )
.setParameter( 1, eOrig.tsColumnDefinition );
final Entity eQueriedWithParameter = (Entity) queryWithParameter.uniqueResult();
assertNotNull( eQueriedWithParameter );
s.getTransaction().commit();
@ -163,8 +163,8 @@ public class MySQL57TimestampPropertyTest extends BaseCoreFunctionalTestCase {
s = openSession();
s.getTransaction().begin();
final Query queryWithTimestamp =
s.createQuery( "from MySQL57TimestampPropertyTest$Entity where tsColumnDefinition=?" )
.setTimestamp( 0, eOrig.tsColumnDefinition );
s.createQuery( "from Entity where tsColumnDefinition= ?1" )
.setTimestamp( 1, eOrig.tsColumnDefinition );
final Entity eQueriedWithTimestamp = (Entity) queryWithTimestamp.uniqueResult();
assertNotNull( eQueriedWithTimestamp );
s.getTransaction().commit();
@ -182,7 +182,7 @@ public class MySQL57TimestampPropertyTest extends BaseCoreFunctionalTestCase {
return new Class[] { Entity.class };
}
@javax.persistence.Entity
@javax.persistence.Entity(name = "Entity")
public static class Entity {
@GeneratedValue
@Id

View File

@ -21,7 +21,7 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
@ -41,7 +41,7 @@ import static org.mockito.Mockito.verify;
/**
* @author Vlad Mihalcea
*/
@SkipForDialect(MariaDBDialect.class)
@SkipForDialect(MySQL5Dialect.class)
public class JdbcTimeCustomTimeZoneTest
extends BaseNonConfigCoreFunctionalTestCase {

View File

@ -18,7 +18,7 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
@ -38,7 +38,7 @@ import static org.mockito.Mockito.verify;
/**
* @author Vlad Mihalcea
*/
@SkipForDialect(MariaDBDialect.class)
@SkipForDialect(MySQL5Dialect.class)
public class JdbcTimestampCustomSessionLevelTimeZoneTest
extends BaseNonConfigCoreFunctionalTestCase {

View File

@ -18,7 +18,7 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
@ -38,7 +38,7 @@ import static org.mockito.Mockito.verify;
/**
* @author Vlad Mihalcea
*/
@SkipForDialect(MariaDBDialect.class)
@SkipForDialect(MySQL5Dialect.class)
public class JdbcTimestampCustomTimeZoneTest
extends BaseNonConfigCoreFunctionalTestCase {

View File

@ -13,12 +13,14 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.dialect.MySQL8Dialect;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.testing.jdbc.ConnectionProviderDelegate;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernateSessionBuilder;
@ -27,7 +29,7 @@ import static org.junit.Assert.assertEquals;
/**
* @author Vlad Mihalcea
*/
@RequiresDialect(MySQLDialect.class)
@RequiresDialect(MySQL5Dialect.class)
public class LocalDateCustomSessionLevelTimeZoneTest
extends BaseNonConfigCoreFunctionalTestCase {
@ -44,7 +46,14 @@ public class LocalDateCustomSessionLevelTimeZoneTest
else if(!url.endsWith( "&" )) {
url += "&";
}
url += "zeroDateTimeBehavior=convertToNull&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Europe/Berlin";
String zeroDateTimeBehavior = "convertToNull";
if ( Dialect.getDialect() instanceof MySQL8Dialect ) {
zeroDateTimeBehavior = "CONVERT_TO_NULL";
}
url += "zeroDateTimeBehavior=" + zeroDateTimeBehavior + "&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Europe/Berlin";
configurationValues.put( AvailableSettings.URL, url);
super.configure( configurationValues );

View File

@ -14,6 +14,7 @@ import javax.persistence.Id;
import org.hibernate.dialect.AbstractHANADialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQL8Dialect;
import org.hibernate.dialect.Oracle9iDialect;
import org.hibernate.testing.SkipForDialect;
@ -29,6 +30,7 @@ import static org.junit.Assert.assertEquals;
*/
@TestForIssue(jiraKey = "HHH-10465")
@SkipForDialect(MariaDBDialect.class)
@SkipForDialect(MySQL8Dialect.class)
@SkipForDialect(value = Oracle9iDialect.class, comment = "Oracle date does not support milliseconds ")
@SkipForDialect(value = AbstractHANADialect.class, comment = "HANA date does not support milliseconds ")
public class TimeAndTimestampTest extends BaseNonConfigCoreFunctionalTestCase {
@ -45,15 +47,15 @@ public class TimeAndTimestampTest extends BaseNonConfigCoreFunctionalTestCase {
doInHibernate( this::sessionFactory, session -> {
Event event = new Event();
event.id = 1L;
event.timeValue = new Time( 123 );
event.timestampValue = new Timestamp( 456 );
event.timeValue = new Time( 12356 );
event.timestampValue = new Timestamp( 45678 );
session.persist( event );
} );
doInHibernate( this::sessionFactory, session -> {
Event event = session.find( Event.class, 1L );
assertEquals(123, event.timeValue.getTime() % TimeUnit.DAYS.toMillis( 1 ));
assertEquals(456, event.timestampValue.getTime() % TimeUnit.DAYS.toMillis( 1 ));
assertEquals(12356, event.timeValue.getTime() % TimeUnit.DAYS.toMillis( 1 ));
assertEquals(45678, event.timestampValue.getTime() % TimeUnit.DAYS.toMillis( 1 ));
} );
}

View File

@ -230,6 +230,12 @@ abstract public class DialectChecks {
}
}
public static class SupportNoWait implements DialectCheck {
public boolean isMatch(Dialect dialect) {
return dialect.supportsNoWait();
}
}
public static class SupportDropConstraints implements DialectCheck {
public boolean isMatch(Dialect dialect) {
return dialect.dropConstraints();

View File

@ -78,9 +78,12 @@ public class ExceptionUtil {
}
else {
Throwable rootCause = ExceptionUtil.rootCause( e );
if ( rootCause != null && (
if (
rootCause != null && (
rootCause.getMessage().contains( "timeout" ) ||
rootCause.getMessage().contains( "timed out" ) )
rootCause.getMessage().contains( "timed out" ) ||
rootCause.getMessage().contains( "lock(s) could not be acquired" )
)
) {
return true;
}