HHH-12252 - Add Agroal to ConnectionProviderInitiator
This commit is contained in:
parent
d29e710c78
commit
1b2424c211
|
@ -9,6 +9,7 @@ hibernate-core:: The main (core) Hibernate module. Defines its ORM features and
|
||||||
hibernate-envers:: Hibernate's historical entity versioning feature
|
hibernate-envers:: Hibernate's historical entity versioning feature
|
||||||
hibernate-spatial:: Hibernate's Spatial/GIS data-type support
|
hibernate-spatial:: Hibernate's Spatial/GIS data-type support
|
||||||
hibernate-osgi:: Hibernate support for running in OSGi containers.
|
hibernate-osgi:: Hibernate support for running in OSGi containers.
|
||||||
|
hibernate-agroal:: Integrates the http://agroal.github.io/[Agroal] connection pooling library into Hibernate
|
||||||
hibernate-c3p0:: Integrates the http://www.mchange.com/projects/c3p0/[C3P0] connection pooling library into Hibernate
|
hibernate-c3p0:: Integrates the http://www.mchange.com/projects/c3p0/[C3P0] connection pooling library into Hibernate
|
||||||
hibernate-hikaricp:: Integrates the http://brettwooldridge.github.io/HikariCP/[HikariCP] connection pooling library into Hibernate
|
hibernate-hikaricp:: Integrates the http://brettwooldridge.github.io/HikariCP/[HikariCP] connection pooling library into Hibernate
|
||||||
hibernate-vibur:: Integrates the http://www.vibur.org/[Vibur DBCP] connection pooling library into Hibernate
|
hibernate-vibur:: Integrates the http://www.vibur.org/[Vibur DBCP] connection pooling library into Hibernate
|
||||||
|
|
|
@ -19,8 +19,9 @@ Hibernate will internally determine which `ConnectionProvider` to use based on t
|
||||||
4. else if any setting prefixed by `hibernate.proxool.` is set -> <<database-connectionprovider-proxool>>
|
4. else if any setting prefixed by `hibernate.proxool.` is set -> <<database-connectionprovider-proxool>>
|
||||||
5. else if any setting prefixed by `hibernate.hikari.` is set -> <<database-connectionprovider-hikari>>
|
5. else if any setting prefixed by `hibernate.hikari.` is set -> <<database-connectionprovider-hikari>>
|
||||||
6. else if any setting prefixed by `hibernate.vibur.` is set -> <<database-connectionprovider-vibur>>
|
6. else if any setting prefixed by `hibernate.vibur.` is set -> <<database-connectionprovider-vibur>>
|
||||||
7. else if `hibernate.connection.url` is set -> <<database-connectionprovider-drivermanager>>
|
7. else if any setting prefixed by `hibernate.agroal.` is set -> <<database-connectionprovider-agroal>>
|
||||||
8. else -> <<database-connectionprovider-provided>>
|
8. else if `hibernate.connection.url` is set -> <<database-connectionprovider-drivermanager>>
|
||||||
|
9. else -> <<database-connectionprovider-provided>>
|
||||||
|
|
||||||
[[database-connectionprovider-datasource]]
|
[[database-connectionprovider-datasource]]
|
||||||
=== Using DataSources
|
=== Using DataSources
|
||||||
|
@ -137,6 +138,26 @@ Additionally, this `ConnectionProvider` will pick up the following Hibernate-spe
|
||||||
`hibernate.connection.isolation`:: Mapped to Vibur's `defaultTransactionIsolationValue` setting. See <<ConnectionProvider support for transaction isolation setting>>.
|
`hibernate.connection.isolation`:: Mapped to Vibur's `defaultTransactionIsolationValue` setting. See <<ConnectionProvider support for transaction isolation setting>>.
|
||||||
`hibernate.connection.autocommit`:: Mapped to Vibur's `defaultAutoCommit` setting
|
`hibernate.connection.autocommit`:: Mapped to Vibur's `defaultAutoCommit` setting
|
||||||
|
|
||||||
|
[[database-connectionprovider-agroal]]
|
||||||
|
=== Using Agroal
|
||||||
|
|
||||||
|
[IMPORTANT]
|
||||||
|
====
|
||||||
|
To use this integration, the application must include the hibernate-agroal module jar (as well as its dependencies) on the classpath.
|
||||||
|
====
|
||||||
|
|
||||||
|
Hibernate also provides support for applications to use http://agroal.github.io/[Agroal] connection pool.
|
||||||
|
|
||||||
|
Set all of your Agroal settings in Hibernate prefixed by `hibernate.agroal.` and this `ConnectionProvider` will pick them up and pass them along to Agroal connection pool.
|
||||||
|
Additionally, this `ConnectionProvider` will pick up the following Hibernate-specific properties and map them to the corresponding Agroal ones (any `hibernate.agroal.` prefixed ones have precedence):
|
||||||
|
|
||||||
|
`hibernate.connection.driver_class`:: Mapped to Agroal's `driverClassName` setting
|
||||||
|
`hibernate.connection.url`:: Mapped to Agroal's `jdbcUrl` setting
|
||||||
|
`hibernate.connection.username`:: Mapped to Agroal's `principal` setting
|
||||||
|
`hibernate.connection.password`:: Mapped to Agroal's `credential` setting
|
||||||
|
`hibernate.connection.isolation`:: Mapped to Agroal's `jdbcTransactionIsolation` setting. See <<ConnectionProvider support for transaction isolation setting>>.
|
||||||
|
`hibernate.connection.autocommit`:: Mapped to Agroal's `autoCommit` setting
|
||||||
|
|
||||||
[[database-connectionprovider-drivermanager]]
|
[[database-connectionprovider-drivermanager]]
|
||||||
=== Using Hibernate's built-in (and unsupported) pooling
|
=== Using Hibernate's built-in (and unsupported) pooling
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,8 @@ ext {
|
||||||
proxool: "proxool:proxool:0.8.3",
|
proxool: "proxool:proxool:0.8.3",
|
||||||
hikaricp: "com.zaxxer:HikariCP:2.5.1",
|
hikaricp: "com.zaxxer:HikariCP:2.5.1",
|
||||||
vibur: "org.vibur:vibur-dbcp:21.2",
|
vibur: "org.vibur:vibur-dbcp:21.2",
|
||||||
|
agroal_api: "io.agroal:agroal-api:0.4-SNAPSHOT",
|
||||||
|
agroal_pool: "io.agroal:agroal-pool:0.4-SNAPSHOT",
|
||||||
|
|
||||||
cdi: "javax.enterprise:cdi-api:${cdiVersion}",
|
cdi: "javax.enterprise:cdi-api:${cdiVersion}",
|
||||||
weld: "org.jboss.weld.se:weld-se-shaded:${weldVersion}",
|
weld: "org.jboss.weld.se:weld-se-shaded:${weldVersion}",
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* 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>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply from: rootProject.file( 'gradle/published-java-module.gradle' )
|
||||||
|
|
||||||
|
description = 'Integration for Agroal as a ConnectionProvider for Hibernate ORM'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project( ':hibernate-core' )
|
||||||
|
compile( libraries.agroal_api )
|
||||||
|
runtime( libraries.agroal_pool )
|
||||||
|
testCompile project( ':hibernate-testing' )
|
||||||
|
testCompile(libraries.mockito)
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* 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.agroal.internal;
|
||||||
|
|
||||||
|
import io.agroal.api.AgroalDataSource;
|
||||||
|
import io.agroal.api.configuration.AgroalConnectionFactoryConfiguration;
|
||||||
|
import io.agroal.api.configuration.supplier.AgroalConnectionFactoryConfigurationSupplier;
|
||||||
|
import io.agroal.api.configuration.supplier.AgroalPropertiesReader;
|
||||||
|
import io.agroal.api.security.NamePrincipal;
|
||||||
|
import io.agroal.api.security.SimplePassword;
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator;
|
||||||
|
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||||
|
import org.hibernate.service.UnknownUnwrapTypeException;
|
||||||
|
import org.hibernate.service.spi.Configurable;
|
||||||
|
import org.hibernate.service.spi.Stoppable;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConnectionProvider based on Agroal connection pool
|
||||||
|
* To use this ConnectionProvider set: <pre> hibernate.connection.provider_class AgroalConnectionProvider </pre>
|
||||||
|
* ( @see AvailableSettings#CONNECTION_PROVIDER )
|
||||||
|
*
|
||||||
|
* Usual hibernate properties are supported:
|
||||||
|
* <pre>
|
||||||
|
* hibernate.connection.driver_class
|
||||||
|
* hibernate.connection.url
|
||||||
|
* hibernate.connection.username
|
||||||
|
* hibernate.connection.password
|
||||||
|
* hibernate.connection.autocommit
|
||||||
|
* hibernate.connection.isolation
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Other configuration options are available, using the <pre>hibernate.agroal</pre> prefix ( @see AgroalPropertiesReader )
|
||||||
|
*
|
||||||
|
* @author Luis Barreiro
|
||||||
|
*/
|
||||||
|
public class AgroalConnectionProvider implements ConnectionProvider, Configurable, Stoppable {
|
||||||
|
|
||||||
|
public static final String CONFIG_PREFIX = "hibernate.agroal.";
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final Logger LOGGER = Logger.getLogger( AgroalConnectionProvider.class );
|
||||||
|
private AgroalDataSource agroalDataSource = null;
|
||||||
|
|
||||||
|
// --- Configurable
|
||||||
|
|
||||||
|
private static void resolveIsolationSetting(Map<String, String> properties, AgroalConnectionFactoryConfigurationSupplier cf) {
|
||||||
|
Integer isolation = ConnectionProviderInitiator.extractIsolation( properties );
|
||||||
|
if ( isolation != null ) {
|
||||||
|
// Agroal resolves transaction isolation from the 'nice' name
|
||||||
|
String isolationString = ConnectionProviderInitiator.toIsolationNiceName( isolation );
|
||||||
|
cf.jdbcTransactionIsolation( AgroalConnectionFactoryConfiguration.TransactionIsolation.valueOf( isolationString ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void copyProperty(Map<String, String> properties, String key, Consumer<T> consumer, Function<String, T> converter) {
|
||||||
|
String value = properties.get( key );
|
||||||
|
if ( value != null ) {
|
||||||
|
consumer.accept( converter.apply( value ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public void configure(Map props) throws HibernateException {
|
||||||
|
LOGGER.debug( "Configuring Agroal" );
|
||||||
|
try {
|
||||||
|
AgroalPropertiesReader agroalProperties = new AgroalPropertiesReader( CONFIG_PREFIX ).readProperties( props );
|
||||||
|
agroalProperties.modify().connectionPoolConfiguration( cp -> cp.connectionFactoryConfiguration( cf -> {
|
||||||
|
copyProperty( props, AvailableSettings.DRIVER, cf::driverClassName, Function.identity() );
|
||||||
|
copyProperty( props, AvailableSettings.URL, cf::jdbcUrl, Function.identity() );
|
||||||
|
copyProperty( props, AvailableSettings.USER, cf::principal, NamePrincipal::new );
|
||||||
|
copyProperty( props, AvailableSettings.PASS, cf::credential, SimplePassword::new );
|
||||||
|
copyProperty( props, AvailableSettings.AUTOCOMMIT, cf::autoCommit, Boolean::valueOf );
|
||||||
|
resolveIsolationSetting( props, cf );
|
||||||
|
return cf;
|
||||||
|
} ) );
|
||||||
|
|
||||||
|
agroalDataSource = AgroalDataSource.from( agroalProperties );
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
throw new HibernateException( e );
|
||||||
|
}
|
||||||
|
LOGGER.debug( "Agroal Configured" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ConnectionProvider
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection getConnection() throws SQLException {
|
||||||
|
return agroalDataSource == null ? null : agroalDataSource.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeConnection(Connection conn) throws SQLException {
|
||||||
|
conn.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAggressiveRelease() {
|
||||||
|
// Agroal supports integration with Narayana as the JTA provider, that would enable aggressive release
|
||||||
|
// That logic is similar with what Hibernate does (however with better performance since it's integrated in the pool)
|
||||||
|
// and therefore that integration is not leveraged right now.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings( "rawtypes" )
|
||||||
|
public boolean isUnwrappableAs(Class unwrapType) {
|
||||||
|
return ConnectionProvider.class.equals( unwrapType ) || AgroalConnectionProvider.class.isAssignableFrom( unwrapType ) || DataSource.class.isAssignableFrom( unwrapType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public <T> T unwrap(Class<T> unwrapType) {
|
||||||
|
if ( ConnectionProvider.class.equals( unwrapType ) || AgroalConnectionProvider.class.isAssignableFrom( unwrapType ) ) {
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
if ( DataSource.class.isAssignableFrom( unwrapType ) ) {
|
||||||
|
return (T) agroalDataSource;
|
||||||
|
}
|
||||||
|
throw new UnknownUnwrapTypeException( unwrapType );
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Stoppable
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
agroalDataSource.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.agroal.internal;
|
||||||
|
|
||||||
|
import org.hibernate.boot.registry.selector.SimpleStrategyRegistrationImpl;
|
||||||
|
import org.hibernate.boot.registry.selector.StrategyRegistration;
|
||||||
|
import org.hibernate.boot.registry.selector.StrategyRegistrationProvider;
|
||||||
|
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the {@link AgroalConnectionProvider} to the
|
||||||
|
* {@link org.hibernate.boot.registry.selector.spi.StrategySelector} service.
|
||||||
|
*
|
||||||
|
* @author Luis Barreiro
|
||||||
|
*/
|
||||||
|
public class StrategyRegistrationProviderImpl implements StrategyRegistrationProvider {
|
||||||
|
|
||||||
|
private static final List<StrategyRegistration> REGISTRATIONS = Collections.singletonList(
|
||||||
|
new SimpleStrategyRegistrationImpl<>(
|
||||||
|
ConnectionProvider.class,
|
||||||
|
AgroalConnectionProvider.class,
|
||||||
|
AgroalConnectionProvider.class.getSimpleName(),
|
||||||
|
"agroal",
|
||||||
|
"Agroal",
|
||||||
|
// for consistency's sake
|
||||||
|
"org.hibernate.connection.AgroalConnectionProvider"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<StrategyRegistration> getStrategyRegistrations() {
|
||||||
|
return REGISTRATIONS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* 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>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of ConnectionProvider using Agroal.
|
||||||
|
*/
|
||||||
|
package org.hibernate.agroal.internal;
|
|
@ -0,0 +1,13 @@
|
||||||
|
#
|
||||||
|
# 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>.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# 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>.
|
||||||
|
#
|
||||||
|
org.hibernate.agroal.internal.StrategyRegistrationProviderImpl
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
~ 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>.
|
||||||
|
-->
|
||||||
|
<blueprint default-activation="eager"
|
||||||
|
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
|
||||||
|
<bean id="strategyRegistrationProvider" class="org.hibernate.agroal.internal.StrategyRegistrationProviderImpl"/>
|
||||||
|
<service ref="strategyRegistrationProvider" interface="org.hibernate.boot.registry.selector.StrategyRegistrationProvider"/>
|
||||||
|
|
||||||
|
</blueprint>
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* 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.test.agroal;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess;
|
||||||
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
|
import org.hibernate.agroal.internal.AgroalConnectionProvider;
|
||||||
|
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brett Meyer
|
||||||
|
*/
|
||||||
|
public class AgroalConnectionProviderTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAgroalConnectionProvider() throws Exception {
|
||||||
|
JdbcServices jdbcServices = serviceRegistry().getService( JdbcServices.class );
|
||||||
|
ConnectionProviderJdbcConnectionAccess connectionAccess = assertTyping(
|
||||||
|
ConnectionProviderJdbcConnectionAccess.class,
|
||||||
|
jdbcServices.getBootstrapJdbcConnectionAccess()
|
||||||
|
);
|
||||||
|
assertTyping( AgroalConnectionProvider.class, connectionAccess.getConnectionProvider() );
|
||||||
|
|
||||||
|
AgroalConnectionProvider agroalConnectionProvider = (AgroalConnectionProvider) connectionAccess.getConnectionProvider();
|
||||||
|
// For simplicity's sake, using the following in hibernate.properties:
|
||||||
|
// hibernate.agroal.maxSize 2
|
||||||
|
// hibernate.agroal.minSize 2
|
||||||
|
List<Connection> conns = new ArrayList<>();
|
||||||
|
for ( int i = 0; i < 2; i++ ) {
|
||||||
|
Connection conn = agroalConnectionProvider.getConnection();
|
||||||
|
assertNotNull( conn );
|
||||||
|
assertFalse( conn.isClosed() );
|
||||||
|
conns.add( conn );
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
agroalConnectionProvider.getConnection();
|
||||||
|
fail( "SQLException expected -- no more connections should have been available in the pool." );
|
||||||
|
}
|
||||||
|
catch (SQLException e) {
|
||||||
|
// expected
|
||||||
|
assertTrue( e.getMessage().contains( "timeout" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( Connection conn : conns ) {
|
||||||
|
agroalConnectionProvider.closeConnection( conn );
|
||||||
|
assertTrue( conn.isClosed() );
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseSessionFactory();
|
||||||
|
|
||||||
|
try {
|
||||||
|
agroalConnectionProvider.getConnection();
|
||||||
|
fail( "Exception expected -- the pool should have been shutdown." );
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// expected
|
||||||
|
assertTrue( e.getMessage().contains( "closed" ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* 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.test.agroal;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
|
import org.hibernate.test.agroal.util.PreparedStatementSpyConnectionProvider;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
public class AgroalSkipAutoCommitTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
private PreparedStatementSpyConnectionProvider connectionProvider = new PreparedStatementSpyConnectionProvider();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(Configuration configuration) {
|
||||||
|
configuration.getProperties().put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider );
|
||||||
|
configuration.getProperties().put( AvailableSettings.CONNECTION_PROVIDER_DISABLES_AUTOCOMMIT, Boolean.TRUE );
|
||||||
|
configuration.getProperties().put( AvailableSettings.AUTOCOMMIT, Boolean.FALSE.toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseSessionFactory() {
|
||||||
|
super.releaseSessionFactory();
|
||||||
|
connectionProvider.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[]{ City.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
connectionProvider.clear();
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
City city = new City();
|
||||||
|
city.setId( 1L );
|
||||||
|
city.setName( "Cluj-Napoca" );
|
||||||
|
session.persist( city );
|
||||||
|
|
||||||
|
assertTrue( connectionProvider.getAcquiredConnections().isEmpty() );
|
||||||
|
assertTrue( connectionProvider.getReleasedConnections().isEmpty() );
|
||||||
|
} );
|
||||||
|
verifyConnections();
|
||||||
|
|
||||||
|
connectionProvider.clear();
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
City city = session.find( City.class, 1L );
|
||||||
|
assertEquals( "Cluj-Napoca", city.getName() );
|
||||||
|
} );
|
||||||
|
verifyConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyConnections() {
|
||||||
|
assertTrue( connectionProvider.getAcquiredConnections().isEmpty() );
|
||||||
|
|
||||||
|
List<Connection> connections = connectionProvider.getReleasedConnections();
|
||||||
|
assertEquals( 1, connections.size() );
|
||||||
|
Connection connection = connections.get( 0 );
|
||||||
|
try {
|
||||||
|
verify(connection, never()).setAutoCommit( false );
|
||||||
|
}
|
||||||
|
catch (SQLException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "City" )
|
||||||
|
public static class City {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.test.agroal;
|
||||||
|
|
||||||
|
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||||
|
import org.hibernate.agroal.internal.AgroalConnectionProvider;
|
||||||
|
|
||||||
|
import org.hibernate.testing.common.connections.BaseTransactionIsolationConfigTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class AgroalTransactionIsolationConfigTest extends BaseTransactionIsolationConfigTest {
|
||||||
|
@Override
|
||||||
|
protected ConnectionProvider getConnectionProviderUnderTest() {
|
||||||
|
return new AgroalConnectionProvider();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* 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.test.agroal.util;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.agroal.internal.AgroalConnectionProvider;
|
||||||
|
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||||
|
|
||||||
|
import org.mockito.ArgumentMatchers;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.internal.util.MockUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link ConnectionProvider} extends any other ConnectionProvider that would be used by default taken the current configuration properties, and it
|
||||||
|
* intercept the underlying {@link PreparedStatement} method calls.
|
||||||
|
*
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
public class PreparedStatementSpyConnectionProvider extends AgroalConnectionProvider {
|
||||||
|
|
||||||
|
private final Map<PreparedStatement, String> preparedStatementMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private final List<Connection> acquiredConnections = new ArrayList<>( );
|
||||||
|
private final List<Connection> releasedConnections = new ArrayList<>( );
|
||||||
|
|
||||||
|
public PreparedStatementSpyConnectionProvider() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Connection actualConnection() throws SQLException {
|
||||||
|
return super.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection getConnection() throws SQLException {
|
||||||
|
Connection connection = spy( actualConnection() );
|
||||||
|
acquiredConnections.add( connection );
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeConnection(Connection conn) throws SQLException {
|
||||||
|
acquiredConnections.remove( conn );
|
||||||
|
releasedConnections.add( conn );
|
||||||
|
super.closeConnection( conn );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
clear();
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Connection spy(Connection connection) {
|
||||||
|
if ( MockUtil.isMock( connection ) ) {
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
Connection connectionSpy = Mockito.spy( connection );
|
||||||
|
try {
|
||||||
|
Mockito.doAnswer( invocation -> {
|
||||||
|
PreparedStatement statement = (PreparedStatement) invocation.callRealMethod();
|
||||||
|
PreparedStatement statementSpy = Mockito.spy( statement );
|
||||||
|
String sql = (String) invocation.getArguments()[0];
|
||||||
|
preparedStatementMap.put( statementSpy, sql );
|
||||||
|
return statementSpy;
|
||||||
|
} ).when( connectionSpy ).prepareStatement( ArgumentMatchers.anyString() );
|
||||||
|
|
||||||
|
Mockito.doAnswer( invocation -> {
|
||||||
|
Statement statement = (Statement) invocation.callRealMethod();
|
||||||
|
Statement statementSpy = Mockito.spy( statement );
|
||||||
|
return statementSpy;
|
||||||
|
} ).when( connectionSpy ).createStatement();
|
||||||
|
}
|
||||||
|
catch ( SQLException e ) {
|
||||||
|
throw new IllegalArgumentException( e );
|
||||||
|
}
|
||||||
|
return connectionSpy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the recorded PreparedStatements and reset the associated Mocks.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
acquiredConnections.clear();
|
||||||
|
releasedConnections.clear();
|
||||||
|
preparedStatementMap.keySet().forEach( Mockito::reset );
|
||||||
|
preparedStatementMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of current acquired Connections.
|
||||||
|
* @return list of current acquired Connections
|
||||||
|
*/
|
||||||
|
public List<Connection> getAcquiredConnections() {
|
||||||
|
return acquiredConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of current released Connections.
|
||||||
|
* @return list of current released Connections
|
||||||
|
*/
|
||||||
|
public List<Connection> getReleasedConnections() {
|
||||||
|
return releasedConnections;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# 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>.
|
||||||
|
#
|
||||||
|
hibernate.dialect @db.dialect@
|
||||||
|
hibernate.connection.driver_class @jdbc.driver@
|
||||||
|
hibernate.connection.url @jdbc.url@
|
||||||
|
hibernate.connection.username @jdbc.user@
|
||||||
|
hibernate.connection.password @jdbc.pass@
|
||||||
|
|
||||||
|
hibernate.jdbc.batch_size 10
|
||||||
|
hibernate.connection.provider_class AgroalConnectionProvider
|
||||||
|
|
||||||
|
hibernate.agroal.maxSize 2
|
||||||
|
hibernate.agroal.acquisitionTimeout PT1s
|
||||||
|
hibernate.agroal.reapTimeout PT10s
|
|
@ -0,0 +1,60 @@
|
||||||
|
#
|
||||||
|
# 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>.
|
||||||
|
#
|
||||||
|
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||||
|
log4j.appender.stdout.Target=System.out
|
||||||
|
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
|
||||||
|
#log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L (hibernateLoadPlanWalkPath->%X{hibernateLoadPlanWalkPath}) - %m%n
|
||||||
|
|
||||||
|
#log4j.appender.stdout-mdc=org.apache.log4j.ConsoleAppender
|
||||||
|
#log4j.appender.stdout-mdc.Target=System.out
|
||||||
|
#log4j.appender.stdout-mdc.layout=org.apache.log4j.PatternLayout
|
||||||
|
#log4j.appender.stdout-mdc.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L (walk path -> %X{hibernateLoadPlanWalkPath}) - %m%n
|
||||||
|
|
||||||
|
log4j.appender.unclosedSessionFactoryFile=org.apache.log4j.FileAppender
|
||||||
|
log4j.appender.unclosedSessionFactoryFile.append=true
|
||||||
|
log4j.appender.unclosedSessionFactoryFile.file=target/tmp/log/UnclosedSessionFactoryWarnings.log
|
||||||
|
log4j.appender.unclosedSessionFactoryFile.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.unclosedSessionFactoryFile.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
|
||||||
|
|
||||||
|
log4j.rootLogger=info, stdout
|
||||||
|
|
||||||
|
#log4j.logger.org.hibernate.loader.plan=trace, stdout-mdc
|
||||||
|
#log4j.additivity.org.hibernate.loader.plan=false
|
||||||
|
#log4j.logger.org.hibernate.persister.walking=trace, stdout-mdc
|
||||||
|
#log4j.additivity.org.hibernate.persister.walking=false
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.tool.hbm2ddl=trace
|
||||||
|
log4j.logger.org.hibernate.testing.cache=debug
|
||||||
|
|
||||||
|
# SQL Logging - HHH-6833
|
||||||
|
log4j.logger.org.hibernate.SQL=debug
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.type.descriptor.sql.BasicBinder=trace
|
||||||
|
log4j.logger.org.hibernate.type.descriptor.sql.BasicExtractor=trace
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.hql.internal.ast=debug
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.sql.ordering.antlr=debug
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.loader.plan2.build.internal.LoadPlanImpl=debug
|
||||||
|
log4j.logger.org.hibernate.loader.plan2.build.spi.LoadPlanTreePrinter=debug
|
||||||
|
log4j.logger.org.hibernate.loader.plan2.exec.spi.EntityLoadQueryDetails=debug
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.engine.internal.StatisticalLoggingSessionEventListener=info
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.boot.model.source.internal.hbm.ModelBinder=debug
|
||||||
|
log4j.logger.org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry=debug
|
||||||
|
|
||||||
|
|
||||||
|
### When entity copy merge functionality is enabled using:
|
||||||
|
### hibernate.event.merge.entity_copy_observer=log, the following will
|
||||||
|
### provide information about merged entity copies.
|
||||||
|
### log4j.logger.org.hibernate.event.internal.EntityCopyAllowedLoggedObserver=debug
|
||||||
|
|
||||||
|
log4j.logger.org.hibernate.testing.junit4.TestClassMetadata=info, unclosedSessionFactoryFile
|
||||||
|
log4j.logger.org.hibernate.boot.model.process.internal.ScanningCoordinator=debug
|
|
@ -0,0 +1 @@
|
||||||
|
mock-maker-inline
|
|
@ -65,6 +65,11 @@ public class ConnectionProviderInitiator implements StandardServiceInitiator<Con
|
||||||
*/
|
*/
|
||||||
public static final String VIBUR_STRATEGY = "vibur";
|
public static final String VIBUR_STRATEGY = "vibur";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The strategy for agroal connection pooling
|
||||||
|
*/
|
||||||
|
public static final String AGROAL_STRATEGY = "agroal";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No idea. Is this even still used?
|
* No idea. Is this even still used?
|
||||||
*/
|
*/
|
||||||
|
@ -165,6 +170,12 @@ public class ConnectionProviderInitiator implements StandardServiceInitiator<Con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( connectionProvider == null ) {
|
||||||
|
if ( agroalConfigDefined( configurationValues ) ) {
|
||||||
|
connectionProvider = instantiateAgroalProvider( strategySelector );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( connectionProvider == null ) {
|
if ( connectionProvider == null ) {
|
||||||
if ( configurationValues.get( AvailableSettings.URL ) != null ) {
|
if ( configurationValues.get( AvailableSettings.URL ) != null ) {
|
||||||
connectionProvider = new DriverManagerConnectionProviderImpl();
|
connectionProvider = new DriverManagerConnectionProviderImpl();
|
||||||
|
@ -288,6 +299,20 @@ public class ConnectionProviderInitiator implements StandardServiceInitiator<Con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean agroalConfigDefined(Map configValues) {
|
||||||
|
for ( Object key : configValues.keySet() ) {
|
||||||
|
if ( !String.class.isInstance( key ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ( (String) key ).startsWith( "hibernate.agroal." ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private ConnectionProvider instantiateViburProvider(StrategySelector strategySelector) {
|
private ConnectionProvider instantiateViburProvider(StrategySelector strategySelector) {
|
||||||
try {
|
try {
|
||||||
return strategySelector.selectStrategyImplementor( ConnectionProvider.class, VIBUR_STRATEGY ).newInstance();
|
return strategySelector.selectStrategyImplementor( ConnectionProvider.class, VIBUR_STRATEGY ).newInstance();
|
||||||
|
@ -298,6 +323,16 @@ public class ConnectionProviderInitiator implements StandardServiceInitiator<Con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ConnectionProvider instantiateAgroalProvider(StrategySelector strategySelector) {
|
||||||
|
try {
|
||||||
|
return strategySelector.selectStrategyImplementor( ConnectionProvider.class, AGROAL_STRATEGY ).newInstance();
|
||||||
|
}
|
||||||
|
catch ( Exception e ) {
|
||||||
|
LOG.agroalProviderClassNotFound();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the connection properties capable of being passed to the {@link java.sql.DriverManager#getConnection}
|
* Build the connection properties capable of being passed to the {@link java.sql.DriverManager#getConnection}
|
||||||
* forms taking {@link Properties} argument. We seek out all keys in the passed map which start with
|
* forms taking {@link Properties} argument. We seek out all keys in the passed map which start with
|
||||||
|
|
|
@ -1795,4 +1795,10 @@ public interface CoreMessageLogger extends BasicLogger {
|
||||||
Object id
|
Object id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@LogMessage(level = WARN)
|
||||||
|
@Message(value = "Agroal properties were encountered, but the Agroal ConnectionProvider was not found on the classpath; these properties are going to be ignored.",
|
||||||
|
id = 486)
|
||||||
|
void agroalProviderClassNotFound();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ include 'hibernate-c3p0'
|
||||||
include 'hibernate-proxool'
|
include 'hibernate-proxool'
|
||||||
include 'hibernate-hikaricp'
|
include 'hibernate-hikaricp'
|
||||||
include 'hibernate-vibur'
|
include 'hibernate-vibur'
|
||||||
|
include 'hibernate-agroal'
|
||||||
|
|
||||||
include 'hibernate-jcache'
|
include 'hibernate-jcache'
|
||||||
include 'hibernate-ehcache'
|
include 'hibernate-ehcache'
|
||||||
|
|
Loading…
Reference in New Issue