HHH-17003 Race conditions in LazyLoadingConnectionCloseTest and ConnectionsReleaseAutoCommitTest
This commit is contained in:
parent
b561e64fca
commit
d8363d720a
|
@ -6,22 +6,10 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.connections;
|
package org.hibernate.orm.test.connections;
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.DriverManager;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.FetchType;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.JoinColumn;
|
|
||||||
import jakarta.persistence.ManyToOne;
|
|
||||||
import jakarta.persistence.OneToMany;
|
|
||||||
import jakarta.persistence.Query;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import org.hibernate.annotations.Fetch;
|
import org.hibernate.annotations.Fetch;
|
||||||
import org.hibernate.annotations.FetchMode;
|
import org.hibernate.annotations.FetchMode;
|
||||||
|
@ -30,9 +18,7 @@ import org.hibernate.annotations.LazyCollectionOption;
|
||||||
import org.hibernate.annotations.LazyToOne;
|
import org.hibernate.annotations.LazyToOne;
|
||||||
import org.hibernate.annotations.LazyToOneOption;
|
import org.hibernate.annotations.LazyToOneOption;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.orm.test.util.connections.ConnectionCheckingConnectionProvider;
|
||||||
import org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl;
|
|
||||||
import org.hibernate.orm.test.jpa.connection.BaseDataSource;
|
|
||||||
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
@ -41,9 +27,16 @@ import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Query;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Selaron
|
* @author Selaron
|
||||||
|
@ -51,7 +44,7 @@ import static org.mockito.Mockito.spy;
|
||||||
@TestForIssue(jiraKey = "HHH-4808")
|
@TestForIssue(jiraKey = "HHH-4808")
|
||||||
public class LazyLoadingConnectionCloseTest extends EntityManagerFactoryBasedFunctionalTest {
|
public class LazyLoadingConnectionCloseTest extends EntityManagerFactoryBasedFunctionalTest {
|
||||||
|
|
||||||
private ConnectionProviderDecorator connectionProvider;
|
private ConnectionCheckingConnectionProvider connectionProvider;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
@ -69,7 +62,7 @@ public class LazyLoadingConnectionCloseTest extends EntityManagerFactoryBasedFun
|
||||||
|
|
||||||
options.put( AvailableSettings.AUTOCOMMIT, "false" );
|
options.put( AvailableSettings.AUTOCOMMIT, "false" );
|
||||||
|
|
||||||
connectionProvider = new ConnectionProviderDecorator( getDataSource() );
|
connectionProvider = new ConnectionCheckingConnectionProvider();
|
||||||
options.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider );
|
options.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -295,68 +288,4 @@ public class LazyLoadingConnectionCloseTest extends EntityManagerFactoryBasedFun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BaseDataSource getDataSource() {
|
|
||||||
final Properties connectionProps = new Properties();
|
|
||||||
connectionProps.put( "user", Environment.getProperties().getProperty( Environment.USER ) );
|
|
||||||
connectionProps.put( "password", Environment.getProperties().getProperty( Environment.PASS ) );
|
|
||||||
|
|
||||||
final String url = Environment.getProperties().getProperty( Environment.URL );
|
|
||||||
return new BaseDataSource() {
|
|
||||||
@Override
|
|
||||||
public Connection getConnection() throws SQLException {
|
|
||||||
return DriverManager.getConnection( url, connectionProps );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Connection getConnection(String username, String password) throws SQLException {
|
|
||||||
return DriverManager.getConnection( url, connectionProps );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConnectionProviderDecorator extends UserSuppliedConnectionProviderImpl {
|
|
||||||
|
|
||||||
private final DataSource dataSource;
|
|
||||||
|
|
||||||
private int connectionCount;
|
|
||||||
private int openConnections;
|
|
||||||
|
|
||||||
private Connection connection;
|
|
||||||
|
|
||||||
public ConnectionProviderDecorator(DataSource dataSource) {
|
|
||||||
this.dataSource = dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Connection getConnection() throws SQLException {
|
|
||||||
connectionCount++;
|
|
||||||
openConnections++;
|
|
||||||
connection = spy( dataSource.getConnection() );
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closeConnection(Connection connection) throws SQLException {
|
|
||||||
connection.close();
|
|
||||||
openConnections--;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTotalOpenedConnectionCount() {
|
|
||||||
return this.connectionCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCurrentOpenConnections() {
|
|
||||||
return openConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean areAllConnectionClosed() {
|
|
||||||
return openConnections == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
connectionCount = 0;
|
|
||||||
openConnections = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* 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>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//$Id$
|
|
||||||
package org.hibernate.orm.test.jpa.connection;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.SQLFeatureNotSupportedException;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Vlad Mihalcea
|
|
||||||
*/
|
|
||||||
public abstract class BaseDataSource implements DataSource {
|
|
||||||
|
|
||||||
public PrintWriter getLogWriter() throws SQLException {
|
|
||||||
return new PrintWriter( System.out );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLoginTimeout(int seconds) throws SQLException {
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLoginTimeout() throws SQLException {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T unwrap(Class<T> tClass) throws SQLException {
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isWrapperFor(Class<?> aClass) throws SQLException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.orm.test.jpa.connection;
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.DriverManager;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Properties;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import org.hibernate.cfg.Environment;
|
|
||||||
import org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
|
|
||||||
public class ConnectionProviderDecorator extends UserSuppliedConnectionProviderImpl {
|
|
||||||
|
|
||||||
private final DataSource dataSource;
|
|
||||||
|
|
||||||
private int connectionCount;
|
|
||||||
|
|
||||||
public Connection connection;
|
|
||||||
|
|
||||||
public ConnectionProviderDecorator(){
|
|
||||||
String url = Environment.getProperties().getProperty( Environment.URL );
|
|
||||||
|
|
||||||
Properties connectionProps = new Properties();
|
|
||||||
connectionProps.put( "user", Environment.getProperties().getProperty( Environment.USER ) );
|
|
||||||
connectionProps.put( "password", Environment.getProperties().getProperty( Environment.PASS ) );
|
|
||||||
|
|
||||||
dataSource = new BaseDataSource() {
|
|
||||||
@Override
|
|
||||||
public Connection getConnection() throws SQLException {
|
|
||||||
return DriverManager.getConnection( url, connectionProps );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Connection getConnection(String username, String password) throws SQLException {
|
|
||||||
return DriverManager.getConnection( url, connectionProps );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Connection getConnection() throws SQLException {
|
|
||||||
connectionCount++;
|
|
||||||
connection = spy( dataSource.getConnection() );
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closeConnection(Connection connection) throws SQLException {
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getConnectionCount() {
|
|
||||||
return this.connectionCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
connectionCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,15 +6,11 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.jpa.connection;
|
package org.hibernate.orm.test.jpa.connection;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.dialect.H2Dialect;
|
import org.hibernate.dialect.H2Dialect;
|
||||||
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.orm.test.util.connections.ConnectionCheckingConnectionProvider;
|
||||||
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||||
|
@ -23,11 +19,12 @@ import org.hibernate.testing.orm.junit.RequiresDialect;
|
||||||
import org.hibernate.testing.orm.junit.Setting;
|
import org.hibernate.testing.orm.junit.Setting;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.mockito.Mockito;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Vlad Mihalcea
|
* @author Vlad Mihalcea
|
||||||
|
@ -36,30 +33,30 @@ import static org.mockito.Mockito.verify;
|
||||||
@RequiresDialect(H2Dialect.class)
|
@RequiresDialect(H2Dialect.class)
|
||||||
@Jpa(
|
@Jpa(
|
||||||
annotatedClasses = { ConnectionsReleaseAutoCommitTest.Thing.class },
|
annotatedClasses = { ConnectionsReleaseAutoCommitTest.Thing.class },
|
||||||
integrationSettings = @Setting(name = AvailableSettings.CONNECTION_PROVIDER, value = "org.hibernate.orm.test.jpa.connection.ConnectionProviderDecorator")
|
integrationSettings = @Setting(name = AvailableSettings.CONNECTION_PROVIDER, value = "org.hibernate.orm.test.util.connections.ConnectionCheckingConnectionProvider")
|
||||||
)
|
)
|
||||||
public class ConnectionsReleaseAutoCommitTest {
|
public class ConnectionsReleaseAutoCommitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectionAcquisitionCount(EntityManagerFactoryScope scope) throws SQLException {
|
public void testConnectionAcquisitionCount(EntityManagerFactoryScope scope) {
|
||||||
ConnectionProviderDecorator connectionProvider = getConnectionProvider( scope );
|
ConnectionCheckingConnectionProvider connectionProvider = getConnectionProvider( scope );
|
||||||
|
assertTrue( connectionProvider.areAllConnectionClosed() );
|
||||||
connectionProvider.clear();
|
connectionProvider.clear();
|
||||||
|
|
||||||
scope.inTransaction( entityManager -> {
|
scope.inTransaction( entityManager -> {
|
||||||
assertEquals( 1, connectionProvider.getConnectionCount() );
|
assertEquals( 1, connectionProvider.getTotalOpenedConnectionCount() );
|
||||||
Thing thing = new Thing();
|
Thing thing = new Thing();
|
||||||
thing.setId( 1 );
|
thing.setId( 1 );
|
||||||
entityManager.persist( thing );
|
entityManager.persist( thing );
|
||||||
assertEquals( 1, connectionProvider.getConnectionCount() );
|
assertEquals( 1, connectionProvider.getTotalOpenedConnectionCount() );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
assertEquals( 1, connectionProvider.getConnectionCount() );
|
assertEquals( 1, connectionProvider.getTotalOpenedConnectionCount() );
|
||||||
verify( connectionProvider.connection, times( 1 ) ).close();
|
assertTrue( connectionProvider.areAllConnectionClosed() );
|
||||||
Mockito.reset( connectionProvider.connection );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConnectionProviderDecorator getConnectionProvider(EntityManagerFactoryScope scope) {
|
private ConnectionCheckingConnectionProvider getConnectionProvider(EntityManagerFactoryScope scope) {
|
||||||
return (ConnectionProviderDecorator) ( (SessionFactoryImplementor) ( scope
|
return (ConnectionCheckingConnectionProvider) ( (SessionFactoryImplementor) ( scope
|
||||||
.getEntityManagerFactory() ) ).getServiceRegistry().getService( ConnectionProvider.class );
|
.getEntityManagerFactory() ) ).getServiceRegistry().getService( ConnectionProvider.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.util.connections;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple {@link DataSource} implementation useful in various integration tests,
|
||||||
|
* or possibly to be used as base class to extend.
|
||||||
|
*/
|
||||||
|
public class BaseDataSource implements DataSource {
|
||||||
|
|
||||||
|
private final Properties connectionProperties;
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
public BaseDataSource(Properties configuration) {
|
||||||
|
url = configuration.getProperty( Environment.URL );
|
||||||
|
connectionProperties = new Properties();
|
||||||
|
connectionProperties.put( "user", configuration.getProperty( Environment.USER ) );
|
||||||
|
connectionProperties.put( "password", configuration.getProperty( Environment.PASS ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection getConnection() throws SQLException {
|
||||||
|
return DriverManager.getConnection( url, connectionProperties );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection getConnection(String username, String password) {
|
||||||
|
throw new UnsupportedOperationException( "method not supported" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrintWriter getLogWriter() {
|
||||||
|
return new PrintWriter( System.out );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLogWriter(PrintWriter out) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLoginTimeout(int seconds) {
|
||||||
|
throw new UnsupportedOperationException( "method not supported" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLoginTimeout() {
|
||||||
|
throw new UnsupportedOperationException("method not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T unwrap(Class<T> tClass) {
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWrapperFor(Class<?> aClass) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Logger getParentLogger() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,358 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.util.connections;
|
||||||
|
|
||||||
|
import java.sql.Array;
|
||||||
|
import java.sql.Blob;
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.Clob;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DatabaseMetaData;
|
||||||
|
import java.sql.NClob;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLClientInfoException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.SQLWarning;
|
||||||
|
import java.sql.SQLXML;
|
||||||
|
import java.sql.Savepoint;
|
||||||
|
import java.sql.ShardingKey;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.sql.Struct;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base delegate implementation of a {@link java.sql.Connection};
|
||||||
|
* meant to be extended so to override select methods.
|
||||||
|
*/
|
||||||
|
public class ConnectionBaseDelegate implements Connection {
|
||||||
|
|
||||||
|
private final Connection delegate;
|
||||||
|
|
||||||
|
ConnectionBaseDelegate(Connection delegate) {
|
||||||
|
Objects.requireNonNull( delegate );
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All methods below are generated by the IDE as standard delegates:
|
||||||
|
// for maintenance reasons, if you need customizations prefer extending
|
||||||
|
// this class over modifying it.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement createStatement() throws SQLException {
|
||||||
|
return delegate.createStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||||
|
return delegate.prepareStatement( sql );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CallableStatement prepareCall(String sql) throws SQLException {
|
||||||
|
return delegate.prepareCall( sql );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String nativeSQL(String sql) throws SQLException {
|
||||||
|
return delegate.nativeSQL( sql );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAutoCommit(boolean autoCommit) throws SQLException {
|
||||||
|
delegate.setAutoCommit( autoCommit );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getAutoCommit() throws SQLException {
|
||||||
|
return delegate.getAutoCommit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commit() throws SQLException {
|
||||||
|
delegate.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rollback() throws SQLException {
|
||||||
|
delegate.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws SQLException {
|
||||||
|
delegate.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() throws SQLException {
|
||||||
|
return delegate.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DatabaseMetaData getMetaData() throws SQLException {
|
||||||
|
return delegate.getMetaData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadOnly(boolean readOnly) throws SQLException {
|
||||||
|
delegate.setReadOnly( readOnly );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadOnly() throws SQLException {
|
||||||
|
return delegate.isReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCatalog(String catalog) throws SQLException {
|
||||||
|
delegate.setCatalog( catalog );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCatalog() throws SQLException {
|
||||||
|
return delegate.getCatalog();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTransactionIsolation(int level) throws SQLException {
|
||||||
|
delegate.setTransactionIsolation( level );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTransactionIsolation() throws SQLException {
|
||||||
|
return delegate.getTransactionIsolation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SQLWarning getWarnings() throws SQLException {
|
||||||
|
return delegate.getWarnings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearWarnings() throws SQLException {
|
||||||
|
delegate.clearWarnings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||||
|
return delegate.createStatement( resultSetType, resultSetConcurrency );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
|
||||||
|
throws SQLException {
|
||||||
|
return delegate.prepareStatement( sql, resultSetType, resultSetConcurrency );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
|
||||||
|
throws SQLException {
|
||||||
|
return delegate.prepareCall( sql, resultSetType, resultSetConcurrency );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Class<?>> getTypeMap() throws SQLException {
|
||||||
|
return delegate.getTypeMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
|
||||||
|
delegate.setTypeMap( map );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHoldability(int holdability) throws SQLException {
|
||||||
|
delegate.setHoldability( holdability );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHoldability() throws SQLException {
|
||||||
|
return delegate.getHoldability();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Savepoint setSavepoint() throws SQLException {
|
||||||
|
return delegate.setSavepoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Savepoint setSavepoint(String name) throws SQLException {
|
||||||
|
return delegate.setSavepoint( name );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rollback(Savepoint savepoint) throws SQLException {
|
||||||
|
delegate.rollback( savepoint );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
|
||||||
|
delegate.releaseSavepoint( savepoint );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
|
||||||
|
throws SQLException {
|
||||||
|
return delegate.createStatement( resultSetType, resultSetConcurrency, resultSetHoldability );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreparedStatement prepareStatement(
|
||||||
|
String sql,
|
||||||
|
int resultSetType,
|
||||||
|
int resultSetConcurrency,
|
||||||
|
int resultSetHoldability) throws SQLException {
|
||||||
|
return delegate.prepareStatement( sql, resultSetType, resultSetConcurrency, resultSetHoldability );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CallableStatement prepareCall(
|
||||||
|
String sql,
|
||||||
|
int resultSetType,
|
||||||
|
int resultSetConcurrency,
|
||||||
|
int resultSetHoldability) throws SQLException {
|
||||||
|
return delegate.prepareCall( sql, resultSetType, resultSetConcurrency, resultSetHoldability );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
|
||||||
|
return delegate.prepareStatement( sql, autoGeneratedKeys );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
|
||||||
|
return delegate.prepareStatement( sql, columnIndexes );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
|
||||||
|
return delegate.prepareStatement( sql, columnNames );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Clob createClob() throws SQLException {
|
||||||
|
return delegate.createClob();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Blob createBlob() throws SQLException {
|
||||||
|
return delegate.createBlob();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NClob createNClob() throws SQLException {
|
||||||
|
return delegate.createNClob();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SQLXML createSQLXML() throws SQLException {
|
||||||
|
return delegate.createSQLXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(int timeout) throws SQLException {
|
||||||
|
return delegate.isValid( timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setClientInfo(String name, String value) throws SQLClientInfoException {
|
||||||
|
delegate.setClientInfo( name, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setClientInfo(Properties properties) throws SQLClientInfoException {
|
||||||
|
delegate.setClientInfo( properties );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientInfo(String name) throws SQLException {
|
||||||
|
return delegate.getClientInfo( name );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Properties getClientInfo() throws SQLException {
|
||||||
|
return delegate.getClientInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
|
||||||
|
return delegate.createArrayOf( typeName, elements );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
|
||||||
|
return delegate.createStruct( typeName, attributes );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSchema(String schema) throws SQLException {
|
||||||
|
delegate.setSchema( schema );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSchema() throws SQLException {
|
||||||
|
return delegate.getSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void abort(Executor executor) throws SQLException {
|
||||||
|
delegate.abort( executor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
|
||||||
|
delegate.setNetworkTimeout( executor, milliseconds );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNetworkTimeout() throws SQLException {
|
||||||
|
return delegate.getNetworkTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beginRequest() throws SQLException {
|
||||||
|
delegate.beginRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endRequest() throws SQLException {
|
||||||
|
delegate.endRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout)
|
||||||
|
throws SQLException {
|
||||||
|
return delegate.setShardingKeyIfValid( shardingKey, superShardingKey, timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLException {
|
||||||
|
return delegate.setShardingKeyIfValid( shardingKey, timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLException {
|
||||||
|
delegate.setShardingKey( shardingKey, superShardingKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShardingKey(ShardingKey shardingKey) throws SQLException {
|
||||||
|
delegate.setShardingKey( shardingKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||||
|
return delegate.unwrap( iface );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||||
|
return delegate.isWrapperFor( iface );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.util.connections;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
|
import org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link org.hibernate.engine.jdbc.connections.spi.ConnectionProvider} implementation
|
||||||
|
* meant to be configured via {@code AvailableSettings.CONNECTION_PROVIDER} to facilitate
|
||||||
|
* testing of how many connections are being opened and closed.
|
||||||
|
* This implementation is thread-safe, however state could be racy: make sure the scenarios
|
||||||
|
* being tested have completed before assertions are verified.
|
||||||
|
*/
|
||||||
|
public final class ConnectionCheckingConnectionProvider extends UserSuppliedConnectionProviderImpl {
|
||||||
|
|
||||||
|
private final DataSource dataSource = new BaseDataSource( Environment.getProperties() );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the "open" events. Does NOT hold the total number of open connections
|
||||||
|
* existing at a given time, just the amount of times a connection was opened.
|
||||||
|
*/
|
||||||
|
private final AtomicInteger connectionOpenEventCount = new AtomicInteger();
|
||||||
|
|
||||||
|
//Using a Vector just to avoid synchronizing on a bag
|
||||||
|
private final Vector<CheckedConnection> openedConnections = new Vector<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection getConnection() throws SQLException {
|
||||||
|
this.connectionOpenEventCount.incrementAndGet();
|
||||||
|
final CheckedConnection opened = new CheckedConnection( dataSource.getConnection() );
|
||||||
|
this.openedConnections.add( opened );
|
||||||
|
return opened;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeConnection(Connection connection) throws SQLException {
|
||||||
|
connection.close();
|
||||||
|
//cast is also meant to verify we're not being returned a different implementation
|
||||||
|
CheckedConnection wrapper = (CheckedConnection) connection;
|
||||||
|
boolean removed = this.openedConnections.remove( wrapper );
|
||||||
|
if ( !removed ) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Closing a connection which wasn't registered in this ConnectionProviderDecorator" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the counters to zero; it's useful to invoke this after Hibernate
|
||||||
|
* has booted to exclude connections being used during initialization.
|
||||||
|
* @throws IllegalStateException if any unclosed connection are being detected.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
this.connectionOpenEventCount.set( 0 );
|
||||||
|
if ( !areAllConnectionClosed() ) {
|
||||||
|
throw new IllegalStateException( "Resetting test helper while not all connections have been closed yet" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the count of connections which are currently open.
|
||||||
|
*/
|
||||||
|
public int getCurrentOpenConnections() {
|
||||||
|
return this.openedConnections.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} iff all known connections that have been opened are now closed.
|
||||||
|
*/
|
||||||
|
public boolean areAllConnectionClosed() {
|
||||||
|
return this.openedConnections.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return This returns the count of connections that have been opened since
|
||||||
|
* construction, or since the last time method {@link #clear()} has
|
||||||
|
* been invoked. N.B. this count includes connections that have since been closed.
|
||||||
|
*/
|
||||||
|
public int getTotalOpenedConnectionCount() {
|
||||||
|
return this.connectionOpenEventCount.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CheckedConnection extends ConnectionBaseDelegate {
|
||||||
|
|
||||||
|
private final AtomicBoolean delegateWasClosed = new AtomicBoolean( false );
|
||||||
|
|
||||||
|
private CheckedConnection(Connection delegate) {
|
||||||
|
super( delegate );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation Note: closing the connection by invoking this method directly will not
|
||||||
|
* unregister it from the #openedConnections vector above:
|
||||||
|
* this implies that there could be a mismatch, and we leverage this to spot connections
|
||||||
|
* that have been closed but not using the appropriate method:
|
||||||
|
* {@link org.hibernate.engine.jdbc.connections.spi.ConnectionProvider#closeConnection(Connection)}
|
||||||
|
* @throws SQLException on any exception during the close operation
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() throws SQLException {
|
||||||
|
super.close();
|
||||||
|
//Safeguard against closing multiple times:
|
||||||
|
if ( !this.delegateWasClosed.compareAndSet( false, true ) ) {
|
||||||
|
throw new IllegalStateException( "Was already closed?!" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue