HHH-11617 - Statement leak in case of 'SQLGrammarException: could not extract ResultSet'
This commit is contained in:
parent
f8570017df
commit
eef8a48ce4
|
@ -443,10 +443,10 @@ public abstract class AbstractLoadPlanBasedLoader {
|
||||||
}
|
}
|
||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
catch ( SQLException sqle ) {
|
catch (SQLException | HibernateException ex) {
|
||||||
session.getJdbcCoordinator().getResourceRegistry().release( st );
|
session.getJdbcCoordinator().getResourceRegistry().release( st );
|
||||||
session.getJdbcCoordinator().afterStatementExecution();
|
session.getJdbcCoordinator().afterStatementExecution();
|
||||||
throw sqle;
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.lock;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import javax.persistence.LockModeType;
|
||||||
|
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.jpa.AvailableSettings;
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
|
import org.hibernate.testing.DialectChecks;
|
||||||
|
import org.hibernate.testing.RequiresDialectFeature;
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.jdbc.PreparedStatementSpyConnectionProvider;
|
||||||
|
import org.hibernate.testing.transaction.TransactionUtil;
|
||||||
|
import org.hibernate.testing.util.ExceptionUtil;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrea Boriero
|
||||||
|
*/
|
||||||
|
public class StatementIsClosedAfterALockExceptionTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
private static final PreparedStatementSpyConnectionProvider CONNECTION_PROVIDER = new PreparedStatementSpyConnectionProvider();
|
||||||
|
|
||||||
|
private Integer lockId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map getConfig() {
|
||||||
|
Map config = super.getConfig();
|
||||||
|
config.put(
|
||||||
|
org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER,
|
||||||
|
CONNECTION_PROVIDER
|
||||||
|
);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
lockId = TransactionUtil.doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
Lock lock = new Lock();
|
||||||
|
lock.setName( "name" );
|
||||||
|
entityManager.persist( lock );
|
||||||
|
return lock.getId();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseResources() {
|
||||||
|
super.releaseResources();
|
||||||
|
CONNECTION_PROVIDER.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 2500) //2.5 seconds
|
||||||
|
@TestForIssue(jiraKey = "HHH-11617")
|
||||||
|
public void testStatementIsClosed() {
|
||||||
|
|
||||||
|
TransactionUtil.doInJPA( this::entityManagerFactory, em1 -> {
|
||||||
|
|
||||||
|
Map<String, Object> properties = new HashMap<>();
|
||||||
|
properties.put( org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT, 0L );
|
||||||
|
Lock lock2 = em1.find( Lock.class, lockId, LockModeType.PESSIMISTIC_WRITE, properties );
|
||||||
|
assertEquals(
|
||||||
|
"lock mode should be PESSIMISTIC_WRITE ",
|
||||||
|
LockModeType.PESSIMISTIC_WRITE,
|
||||||
|
em1.getLockMode( lock2 )
|
||||||
|
);
|
||||||
|
|
||||||
|
TransactionUtil.doInJPA( this::entityManagerFactory, em2 -> {
|
||||||
|
TransactionUtil.setJdbcTimeout( em2.unwrap( Session.class ) );
|
||||||
|
try {
|
||||||
|
em2.find( Lock.class, lockId, LockModeType.PESSIMISTIC_WRITE, properties );
|
||||||
|
fail( "Exception should be thrown" );
|
||||||
|
}
|
||||||
|
catch (Exception lte) {
|
||||||
|
if( !ExceptionUtil.isSqlLockTimeout( lte )) {
|
||||||
|
fail("Should have thrown a Lock timeout exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
for ( PreparedStatement statement : CONNECTION_PROVIDER.getPreparedStatements() ) {
|
||||||
|
assertThat(
|
||||||
|
"A SQL Statement was not closed : " + statement.toString(),
|
||||||
|
statement.isClosed(),
|
||||||
|
is( true )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SQLException e) {
|
||||||
|
fail( e.getMessage() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class[] getAnnotatedClasses() {
|
||||||
|
return new Class[] {
|
||||||
|
Lock.class,
|
||||||
|
UnversionedLock.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,10 +6,12 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.testing.transaction;
|
package org.hibernate.testing.transaction;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -445,32 +447,43 @@ public class TransactionUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set Session or Statement timeout
|
* Set Session or Statement timeout
|
||||||
* @param session Hibernate Session
|
* @param session Hibernate Session
|
||||||
*/
|
*/
|
||||||
public static void setJdbcTimeout(Session session) {
|
public static void setJdbcTimeout(Session session) {
|
||||||
|
setJdbcTimeout( session, TimeUnit.SECONDS.toMillis( 1 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Session or Statement timeout
|
||||||
|
* @param session Hibernate Session
|
||||||
|
*/
|
||||||
|
public static void setJdbcTimeout(Session session, long millis) {
|
||||||
|
|
||||||
session.doWork( connection -> {
|
session.doWork( connection -> {
|
||||||
if ( Dialect.getDialect() instanceof PostgreSQL81Dialect ) {
|
if ( Dialect.getDialect() instanceof PostgreSQL81Dialect ) {
|
||||||
try (Statement st = connection.createStatement()) {
|
try (Statement st = connection.createStatement()) {
|
||||||
st.execute( "SET statement_timeout TO 1000" );
|
//Prepared Statements fail for SET commands
|
||||||
|
st.execute(String.format( "SET statement_timeout TO %d", millis / 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if( Dialect.getDialect() instanceof MySQLDialect ) {
|
else if( Dialect.getDialect() instanceof MySQLDialect ) {
|
||||||
try (Statement st = connection.createStatement()) {
|
try (PreparedStatement st = connection.prepareStatement("SET GLOBAL innodb_lock_wait_timeout = ?")) {
|
||||||
st.execute( "SET GLOBAL innodb_lock_wait_timeout = 1" );
|
st.setLong( 1, TimeUnit.MILLISECONDS.toSeconds( millis ) );
|
||||||
|
st.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( Dialect.getDialect() instanceof H2Dialect ) {
|
else if( Dialect.getDialect() instanceof H2Dialect ) {
|
||||||
try (Statement st = connection.createStatement()) {
|
try (PreparedStatement st = connection.prepareStatement("SET LOCK_TIMEOUT ?")) {
|
||||||
st.execute( "SET LOCK_TIMEOUT 100" );
|
st.setLong( 1, millis / 10 );
|
||||||
|
st.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), 1000 );
|
connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), (int) millis );
|
||||||
}
|
}
|
||||||
catch (Throwable ignore) {
|
catch (Throwable ignore) {
|
||||||
ignore.fillInStackTrace();
|
ignore.fillInStackTrace();
|
||||||
|
|
Loading…
Reference in New Issue