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;
|
||||
}
|
||||
catch ( SQLException sqle ) {
|
||||
catch (SQLException | HibernateException ex) {
|
||||
session.getJdbcCoordinator().getResourceRegistry().release( st );
|
||||
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;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.Statement;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -445,32 +447,43 @@ public class TransactionUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Session or Statement timeout
|
||||
* @param session Hibernate 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 -> {
|
||||
if ( Dialect.getDialect() instanceof PostgreSQL81Dialect ) {
|
||||
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 ) {
|
||||
try (Statement st = connection.createStatement()) {
|
||||
st.execute( "SET GLOBAL innodb_lock_wait_timeout = 1" );
|
||||
try (PreparedStatement st = connection.prepareStatement("SET GLOBAL innodb_lock_wait_timeout = ?")) {
|
||||
st.setLong( 1, TimeUnit.MILLISECONDS.toSeconds( millis ) );
|
||||
st.execute();
|
||||
}
|
||||
}
|
||||
else if( Dialect.getDialect() instanceof H2Dialect ) {
|
||||
try (Statement st = connection.createStatement()) {
|
||||
st.execute( "SET LOCK_TIMEOUT 100" );
|
||||
try (PreparedStatement st = connection.prepareStatement("SET LOCK_TIMEOUT ?")) {
|
||||
st.setLong( 1, millis / 10 );
|
||||
st.execute();
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), 1000 );
|
||||
connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), (int) millis );
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
ignore.fillInStackTrace();
|
||||
|
|
Loading…
Reference in New Issue