Skip to content

Commit 1983123

Browse files
committed
Use a shared connection provider for most tests to reduce test time significantly
1 parent 709ac03 commit 1983123

28 files changed

+378
-79
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.engine.jdbc.connections.internal;
8+
9+
import java.sql.Connection;
10+
import java.sql.SQLException;
11+
12+
/**
13+
* Contract for validating JDBC Connections
14+
*
15+
* @author Christian Beikov
16+
*/
17+
public interface ConnectionValidator {
18+
19+
ConnectionValidator ALWAYS_VALID = connection -> true;
20+
21+
/**
22+
* Checks if the connection is still valid
23+
*
24+
* @return <code>true</code> if the connection is valid, <code>false</code> otherwise
25+
* @throws SQLException when an error happens due to the connection usage leading to a connection close
26+
*/
27+
boolean isValid(Connection connection) throws SQLException;
28+
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
* @author Steve Ebersole
4848
*/
4949
public class DriverManagerConnectionProviderImpl
50-
implements ConnectionProvider, Configurable, Stoppable, ServiceRegistryAwareService {
50+
implements ConnectionProvider, Configurable, Stoppable, ServiceRegistryAwareService, ConnectionValidator {
5151

5252
private static final ConnectionPoolingLogger log = ConnectionPoolingLogger.CONNECTIONS_LOGGER;
5353

@@ -94,7 +94,7 @@ private PooledConnections buildPool(Map configurationValues, ServiceRegistryImpl
9494
pooledConnectionBuilder.initialSize( initialSize );
9595
pooledConnectionBuilder.minSize( minSize );
9696
pooledConnectionBuilder.maxSize( maxSize );
97-
97+
pooledConnectionBuilder.validator( this );
9898
return pooledConnectionBuilder.build();
9999
}
100100

@@ -205,13 +205,20 @@ public <T> T unwrap(Class<T> unwrapType) {
205205
}
206206
}
207207

208+
protected void validateConnectionsReturned() {
209+
int allocationCount = state.pool.allConnections.size() - state.pool.availableConnections.size();
210+
if ( allocationCount != 0 ) {
211+
log.error( "Connection leak detected: there are " + allocationCount + " unclosed connections!");
212+
}
213+
}
208214

209215
// destroy the pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
210216

211217
@Override
212218
public void stop() {
213219
if ( state != null ) {
214220
state.stop();
221+
validateConnectionsReturned();
215222
}
216223
}
217224

@@ -234,6 +241,10 @@ public Properties getConnectionProperties() {
234241
return connectionCreator.getConnectionProperties();
235242
}
236243

244+
@Override
245+
public boolean isValid(Connection connection) throws SQLException {
246+
return true;
247+
}
237248

238249
public static class PooledConnections {
239250

@@ -243,6 +254,7 @@ public static class PooledConnections {
243254
private static final CoreMessageLogger log = CoreLogging.messageLogger( DriverManagerConnectionProviderImpl.class );
244255

245256
private final ConnectionCreator connectionCreator;
257+
private final ConnectionValidator connectionValidator;
246258
private final boolean autoCommit;
247259
private final int minSize;
248260
private final int maxSize;
@@ -253,6 +265,9 @@ private PooledConnections(
253265
Builder builder) {
254266
log.debugf( "Initializing Connection pool with %s Connections", builder.initialSize );
255267
connectionCreator = builder.connectionCreator;
268+
connectionValidator = builder.connectionValidator == null
269+
? ConnectionValidator.ALWAYS_VALID
270+
: builder.connectionValidator;
256271
autoCommit = builder.autoCommit;
257272
maxSize = builder.maxSize;
258273
minSize = builder.minSize;
@@ -283,26 +298,79 @@ else if ( size > maxSize ) {
283298
}
284299

285300
public void add(Connection conn) throws SQLException {
286-
conn.setAutoCommit( true );
287-
conn.clearWarnings();
288-
availableConnections.offer( conn );
301+
final Connection connection = releaseConnection( conn );
302+
if ( connection != null ) {
303+
availableConnections.offer( connection );
304+
}
305+
}
306+
307+
protected Connection releaseConnection(Connection conn) {
308+
Exception t = null;
309+
try {
310+
conn.setAutoCommit( true );
311+
conn.clearWarnings();
312+
if ( connectionValidator.isValid( conn ) ) {
313+
return conn;
314+
}
315+
}
316+
catch (SQLException ex) {
317+
t = ex;
318+
}
319+
closeConnection( conn, t );
320+
log.debug( "Connection release failed. Closing pooled connection", t );
321+
return null;
289322
}
290323

291324
public Connection poll() throws SQLException {
292-
Connection conn = availableConnections.poll();
293-
if ( conn == null ) {
294-
synchronized (allConnections) {
295-
if(allConnections.size() < maxSize) {
296-
addConnections( 1 );
297-
return poll();
325+
Connection conn;
326+
do {
327+
conn = availableConnections.poll();
328+
if ( conn == null ) {
329+
synchronized (allConnections) {
330+
if ( allConnections.size() < maxSize ) {
331+
addConnections( 1 );
332+
return poll();
333+
}
298334
}
335+
throw new HibernateException(
336+
"The internal connection pool has reached its maximum size and no connection is currently available!" );
299337
}
300-
throw new HibernateException( "The internal connection pool has reached its maximum size and no connection is currently available!" );
301-
}
302-
conn.setAutoCommit( autoCommit );
338+
conn = prepareConnection( conn );
339+
} while ( conn == null );
303340
return conn;
304341
}
305342

343+
protected Connection prepareConnection(Connection conn) {
344+
Exception t = null;
345+
try {
346+
conn.setAutoCommit( autoCommit );
347+
if ( connectionValidator.isValid( conn ) ) {
348+
return conn;
349+
}
350+
}
351+
catch (SQLException ex) {
352+
t = ex;
353+
}
354+
closeConnection( conn, t );
355+
log.debug( "Connection preparation failed. Closing pooled connection", t );
356+
return null;
357+
}
358+
359+
protected void closeConnection(Connection conn, Throwable t) {
360+
try {
361+
conn.close();
362+
}
363+
catch (SQLException ex) {
364+
log.unableToCloseConnection( ex );
365+
if ( t != null ) {
366+
t.addSuppressed( ex );
367+
}
368+
}
369+
finally {
370+
allConnections.remove( conn );
371+
}
372+
}
373+
306374
public void close() throws SQLException {
307375
try {
308376
int allocationCount = allConnections.size() - availableConnections.size();
@@ -350,6 +418,7 @@ public String getUrl() {
350418

351419
public static class Builder {
352420
private final ConnectionCreator connectionCreator;
421+
private ConnectionValidator connectionValidator;
353422
private boolean autoCommit;
354423
private int initialSize = 1;
355424
private int minSize = 1;
@@ -375,6 +444,11 @@ public Builder maxSize(int maxSize) {
375444
return this;
376445
}
377446

447+
public Builder validator(ConnectionValidator connectionValidator) {
448+
this.connectionValidator = connectionValidator;
449+
return this;
450+
}
451+
378452
public PooledConnections build() {
379453
return new PooledConnections( this );
380454
}

hibernate-core/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@
2525
import org.hibernate.cfg.AvailableSettings;
2626
import org.hibernate.cfg.Environment;
2727
import org.hibernate.dialect.Dialect;
28+
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
2829
import org.hibernate.engine.spi.SessionFactoryImplementor;
30+
import org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy;
31+
import org.hibernate.hql.spi.id.local.LocalTemporaryTableBulkIdStrategy;
2932
import org.hibernate.jpa.HibernatePersistenceProvider;
3033
import org.hibernate.jpa.boot.spi.Bootstrap;
3134
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
35+
36+
import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl;
3237
import org.hibernate.testing.junit4.BaseUnitTestCase;
3338
import org.junit.After;
3439
import org.junit.Before;
@@ -223,6 +228,14 @@ protected Map getConfig() {
223228
config.put( AvailableSettings.XML_FILE_NAMES, dds );
224229
}
225230

231+
if ( !config.containsKey( Environment.CONNECTION_PROVIDER ) ) {
232+
config.put( GlobalTemporaryTableBulkIdStrategy.DROP_ID_TABLES, "true" );
233+
config.put( LocalTemporaryTableBulkIdStrategy.DROP_ID_TABLES, "true" );
234+
config.put(
235+
AvailableSettings.CONNECTION_PROVIDER,
236+
SharedDriverManagerConnectionProviderImpl.getInstance()
237+
);
238+
}
226239
addConfigOptions( config );
227240
return config;
228241
}

hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/DisableDiscardPersistenceContextOnCloseTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.Map;
1111
import javax.persistence.EntityManager;
1212

13+
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
1314
import org.hibernate.jpa.AvailableSettings;
1415
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
1516
import org.hibernate.jpa.test.Wallet;
@@ -34,6 +35,7 @@ public class DisableDiscardPersistenceContextOnCloseTest extends BaseEntityManag
3435
@Override
3536
protected Map getConfig() {
3637
Map config = super.getConfig();
38+
connectionProvider.setConnectionProvider( (ConnectionProvider) config.get( org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER ) );
3739
config.put( AvailableSettings.DISCARD_PC_ON_CLOSE, "false");
3840
config.put(
3941
org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER,

hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/EnableDiscardPersistenceContextOnCloseTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import javax.persistence.EntityManager;
1111

1212
import org.hibernate.dialect.DB2Dialect;
13+
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
1314
import org.hibernate.jpa.AvailableSettings;
1415
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
1516
import org.hibernate.jpa.test.Wallet;
@@ -35,6 +36,7 @@ public class EnableDiscardPersistenceContextOnCloseTest extends BaseEntityManage
3536
@Override
3637
protected Map getConfig() {
3738
Map config = super.getConfig();
39+
connectionProvider.setConnectionProvider( (ConnectionProvider) config.get( org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER ) );
3840
config.put( AvailableSettings.DISCARD_PC_ON_CLOSE, "true");
3941
config.put(
4042
org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER,

0 commit comments

Comments
 (0)