HHH-11542 - Allow the auto-commit resolution to be configurable for RESOURCE_LOCAL transactions
This commit is contained in:
parent
23aad981a8
commit
ca103dcb28
|
@ -54,6 +54,14 @@ Note that for backwards compatibility, if a https://docs.jboss.org/hibernate/orm
|
|||
|`hibernate.connection.isolation` | `REPEATABLE_READ` or
|
||||
`Connection.TRANSACTION_REPEATABLE_READ` | Names the JDBC connection transaction isolation level.
|
||||
|`hibernate.connection.autocommit` | `true` or `false` (default value) | Names the JDBC connection autocommit mode.
|
||||
|`hibernate.connection.skip_autocommit_check` | `true` or `false` (default value) |
|
||||
|
||||
When using an external non-JTA `DataSource`, it might be that the underlying `DataSource` already disables the autocommit mode,
|
||||
so there is no need to check this `Connection` attribute upon starting a RESOURCE_LOCAL transaction.
|
||||
|
||||
By setting it to `true`, the `Connection` acquisition can be delayed until the first SQL statement is needed to be executed.
|
||||
The connection acquisition delay allows you to reduce the database connection lease time, therefore allowing you to increase the transaction throughput.
|
||||
|
||||
|`hibernate.connection.datasource` | |
|
||||
|
||||
Either a `javax.sql.DataSource` instance or a JNDI name under which to locate the `DataSource`.
|
||||
|
|
|
@ -342,6 +342,14 @@ public interface AvailableSettings {
|
|||
*/
|
||||
String DATASOURCE ="hibernate.connection.datasource";
|
||||
|
||||
/**
|
||||
* Instructs Hibernate to skip the autocommit check for local transactions since
|
||||
* the underlying {@link javax.sql.DataSource} has already disabled autocommit.
|
||||
*
|
||||
* @since 5.2.10
|
||||
*/
|
||||
String SKIP_AUTOCOMMIT_CHECK ="hibernate.connection.skip_autocommit_check";
|
||||
|
||||
/**
|
||||
* Names a prefix used to define arbitrary JDBC connection properties. These properties are passed along to
|
||||
* the {@literal JDBC} provider when creating a connection.
|
||||
|
|
|
@ -62,11 +62,13 @@ public abstract class AbstractLogicalConnectionImplementor implements LogicalCon
|
|||
@Override
|
||||
public void begin() {
|
||||
try {
|
||||
if ( !isSkipAutoCommitCheck() ) {
|
||||
log.trace( "Preparing to begin transaction via JDBC Connection.setAutoCommit(false)" );
|
||||
getConnectionForTransactionManagement().setAutoCommit( false );
|
||||
status = TransactionStatus.ACTIVE;
|
||||
log.trace( "Transaction begun via JDBC Connection.setAutoCommit(false)" );
|
||||
}
|
||||
status = TransactionStatus.ACTIVE;
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new TransactionException( "JDBC begin transaction failed: ", e );
|
||||
}
|
||||
|
@ -136,4 +138,8 @@ public abstract class AbstractLogicalConnectionImplementor implements LogicalCon
|
|||
public TransactionStatus getStatus(){
|
||||
return status;
|
||||
}
|
||||
|
||||
protected boolean isSkipAutoCommitCheck() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@ import java.sql.SQLException;
|
|||
import org.hibernate.ConnectionAcquisitionMode;
|
||||
import org.hibernate.ConnectionReleaseMode;
|
||||
import org.hibernate.ResourceClosedException;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.config.spi.StandardConverters;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||
|
@ -43,6 +46,8 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
|
|||
private transient Connection physicalConnection;
|
||||
private boolean closed;
|
||||
|
||||
private boolean skipAutoCommitCheck;
|
||||
|
||||
public LogicalConnectionManagedImpl(
|
||||
JdbcConnectionAccess jdbcConnectionAccess,
|
||||
JdbcSessionContext jdbcSessionContext) {
|
||||
|
@ -70,6 +75,15 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
|
|||
if ( connectionHandlingMode.getAcquisitionMode() == ConnectionAcquisitionMode.IMMEDIATELY ) {
|
||||
acquireConnectionIfNeeded();
|
||||
}
|
||||
|
||||
ConfigurationService configurationService = jdbcSessionContext.getServiceRegistry()
|
||||
.getService( ConfigurationService.class );
|
||||
|
||||
this.skipAutoCommitCheck = configurationService.getSetting(
|
||||
AvailableSettings.SKIP_AUTOCOMMIT_CHECK,
|
||||
StandardConverters.BOOLEAN,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private PhysicalConnectionHandlingMode determineConnectionHandlingMode(
|
||||
|
@ -251,7 +265,8 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
|
|||
|
||||
@Override
|
||||
public void begin() {
|
||||
initiallyAutoCommit = determineInitialAutoCommitMode( getConnectionForTransactionManagement() );
|
||||
initiallyAutoCommit = !isSkipAutoCommitCheck() && determineInitialAutoCommitMode(
|
||||
getConnectionForTransactionManagement() );
|
||||
super.begin();
|
||||
}
|
||||
|
||||
|
@ -262,4 +277,9 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
|
|||
resetConnection( initiallyAutoCommit );
|
||||
initiallyAutoCommit = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSkipAutoCommitCheck() {
|
||||
return skipAutoCommitCheck;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,15 @@ import java.lang.reflect.Field;
|
|||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.config.spi.StandardConverters;
|
||||
import org.hibernate.engine.jdbc.batch.spi.Batch;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.resource.jdbc.spi.JdbcObserver;
|
||||
import org.hibernate.resource.jdbc.spi.JdbcSessionContext;
|
||||
import org.hibernate.resource.jdbc.spi.JdbcSessionOwner;
|
||||
|
@ -62,6 +67,12 @@ public class JdbcCoordinatorTest {
|
|||
when( serviceRegistry.getService( eq( JdbcServices.class ) ) ).thenReturn(
|
||||
jdbcServices );
|
||||
|
||||
ConfigurationService configurationService = Mockito.mock( ConfigurationService.class );
|
||||
when( serviceRegistry.getService( eq( ConfigurationService.class ) ) ).thenReturn(
|
||||
configurationService );
|
||||
when( configurationService.getSetting(eq( AvailableSettings.SKIP_AUTOCOMMIT_CHECK ), same( StandardConverters.BOOLEAN), eq( false )) )
|
||||
.thenReturn( false );
|
||||
|
||||
SqlExceptionHelper sqlExceptionHelper = Mockito.mock( SqlExceptionHelper.class );
|
||||
when( jdbcServices.getSqlExceptionHelper() ).thenReturn(
|
||||
sqlExceptionHelper );
|
||||
|
|
|
@ -44,7 +44,7 @@ import static org.junit.Assert.fail;
|
|||
@RequiresDialect(H2Dialect.class)
|
||||
public class CriteriaLiteralsTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private PreparedStatementSpyConnectionProvider connectionProvider = new PreparedStatementSpyConnectionProvider();
|
||||
private PreparedStatementSpyConnectionProvider connectionProvider;
|
||||
|
||||
@Override
|
||||
protected Map getConfig() {
|
||||
|
@ -56,6 +56,12 @@ public class CriteriaLiteralsTest extends BaseEntityManagerFunctionalTestCase {
|
|||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEntityManagerFactory() throws Exception {
|
||||
connectionProvider = new PreparedStatementSpyConnectionProvider();
|
||||
super.buildEntityManagerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseResources() {
|
||||
super.releaseResources();
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.resource.transaction.jdbc.autocommit;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.jdbc.PreparedStatementSpyConnectionProvider;
|
||||
import org.hibernate.test.util.ReflectionUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
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 abstract class AbstractSkipAutoCommitTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private PreparedStatementSpyConnectionProvider connectionProvider =
|
||||
new PreparedStatementSpyConnectionProvider() {
|
||||
@Override
|
||||
protected Connection actualConnection() throws SQLException {
|
||||
Connection connection = super.actualConnection();
|
||||
connection.setAutoCommit( false );
|
||||
return connection;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected Map getConfig() {
|
||||
Map config = super.getConfig();
|
||||
|
||||
config.put( AvailableSettings.DATASOURCE, dataSource() );
|
||||
config.put( AvailableSettings.SKIP_AUTOCOMMIT_CHECK, Boolean.TRUE );
|
||||
config.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider );
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
protected abstract DataSource dataSource();
|
||||
|
||||
@Override
|
||||
public void releaseResources() {
|
||||
super.releaseResources();
|
||||
connectionProvider.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
City.class,
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
connectionProvider.clear();
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
City city = new City();
|
||||
city.setId( 1L );
|
||||
city.setName( "Cluj-Napoca" );
|
||||
entityManager.persist( city );
|
||||
|
||||
assertTrue( connectionProvider.getAcquiredConnections().isEmpty() );
|
||||
assertTrue( connectionProvider.getReleasedConnections().isEmpty() );
|
||||
} );
|
||||
verifyConnections();
|
||||
|
||||
connectionProvider.clear();
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
City city = entityManager.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,33 @@
|
|||
/*
|
||||
* 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.resource.transaction.jdbc.autocommit;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.test.util.ReflectionUtil;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
public class H2SkipAutoCommitTest extends AbstractSkipAutoCommitTest {
|
||||
|
||||
@Override
|
||||
protected DataSource dataSource() {
|
||||
DataSource dataSource = ReflectionUtil.newInstance( "org.h2.jdbcx.JdbcDataSource" );
|
||||
ReflectionUtil.setProperty( dataSource, "URL", Environment.getProperties().getProperty( AvailableSettings.URL ) );
|
||||
ReflectionUtil.setProperty( dataSource, "user", Environment.getProperties().getProperty( AvailableSettings.USER ) );
|
||||
ReflectionUtil.setProperty( dataSource, "password", Environment.getProperties().getProperty( AvailableSettings.PASS ) );
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.resource.transaction.jdbc.autocommit;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.MySQLDialect;
|
||||
import org.hibernate.dialect.PostgreSQL81Dialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.test.util.ReflectionUtil;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(MySQLDialect.class)
|
||||
public class MySQLSkipAutoCommitTest extends AbstractSkipAutoCommitTest {
|
||||
|
||||
@Override
|
||||
protected DataSource dataSource() {
|
||||
DataSource dataSource = ReflectionUtil.newInstance( "com.mysql.cj.jdbc.MysqlDataSource" );
|
||||
ReflectionUtil.setProperty( dataSource, "url", Environment.getProperties().getProperty( AvailableSettings.URL ) );
|
||||
ReflectionUtil.setProperty( dataSource, "user", Environment.getProperties().getProperty( AvailableSettings.USER ) );
|
||||
ReflectionUtil.setProperty( dataSource, "password", Environment.getProperties().getProperty( AvailableSettings.PASS ) );
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.resource.transaction.jdbc.autocommit;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.MySQLDialect;
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.test.util.ReflectionUtil;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(Oracle8iDialect.class)
|
||||
public class OracleSkipAutoCommitTest extends AbstractSkipAutoCommitTest {
|
||||
|
||||
@Override
|
||||
protected DataSource dataSource() {
|
||||
DataSource dataSource = ReflectionUtil.newInstance( "oracle.jdbc.pool.OracleDataSource" );
|
||||
ReflectionUtil.setProperty( dataSource, "URL", Environment.getProperties().getProperty( AvailableSettings.URL ) );
|
||||
ReflectionUtil.setProperty( dataSource, "user", Environment.getProperties().getProperty( AvailableSettings.USER ) );
|
||||
ReflectionUtil.setProperty( dataSource, "password", Environment.getProperties().getProperty( AvailableSettings.PASS ) );
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.resource.transaction.jdbc.autocommit;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.dialect.PostgreSQL81Dialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.test.util.ReflectionUtil;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(PostgreSQL81Dialect.class)
|
||||
public class PostgreSQLSkipAutoCommitTest extends AbstractSkipAutoCommitTest {
|
||||
|
||||
@Override
|
||||
protected DataSource dataSource() {
|
||||
DataSource dataSource = ReflectionUtil.newInstance( "org.postgresql.ds.PGSimpleDataSource" );
|
||||
ReflectionUtil.setProperty( dataSource, "url", Environment.getProperties().getProperty( AvailableSettings.URL ) );
|
||||
ReflectionUtil.setProperty( dataSource, "user", Environment.getProperties().getProperty( AvailableSettings.USER ) );
|
||||
ReflectionUtil.setProperty( dataSource, "password", Environment.getProperties().getProperty( AvailableSettings.PASS ) );
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.resource.transaction.jdbc.autocommit;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.PostgreSQL81Dialect;
|
||||
import org.hibernate.dialect.SQLServerDialect;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.test.util.ReflectionUtil;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(SQLServerDialect.class)
|
||||
public class SQLServerSkipAutoCommitTest extends AbstractSkipAutoCommitTest {
|
||||
|
||||
@Override
|
||||
protected DataSource dataSource() {
|
||||
DataSource dataSource = ReflectionUtil.newInstance( "com.microsoft.sqlserver.jdbc.SQLServerDataSource" );
|
||||
ReflectionUtil.setProperty( dataSource, "URL", Environment.getProperties().getProperty( AvailableSettings.URL ) );
|
||||
ReflectionUtil.setProperty( dataSource, "user", Environment.getProperties().getProperty( AvailableSettings.USER ) );
|
||||
ReflectionUtil.setProperty( dataSource, "password", Environment.getProperties().getProperty( AvailableSettings.PASS ) );
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package org.hibernate.test.util;
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
|
@ -62,6 +63,22 @@ public class ReflectionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set target Object field to a certain value
|
||||
* @param target Object whose field is being set
|
||||
* @param fieldName Object field naem to set
|
||||
* @param value the new value for the given field
|
||||
*/
|
||||
public static void setField(Object target, String fieldName, Object value) {
|
||||
try {
|
||||
Field field = getField(target.getClass(), fieldName);
|
||||
field.set( target, value );
|
||||
}
|
||||
catch ( IllegalAccessException e ) {
|
||||
throw new IllegalArgumentException("Field " + fieldName + " could not be set", e );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* New target Object instance using the given arguments
|
||||
* @param constructorSupplier constructor supplier
|
||||
|
@ -80,18 +97,63 @@ public class ReflectionUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set target Object field to a certain value
|
||||
* @param target Object whose field is being set
|
||||
* @param fieldName Object field naem to set
|
||||
* @param value the new value for the given field
|
||||
* New target Object instance using the given Class name
|
||||
* @param className class name
|
||||
* @return new Object instance
|
||||
*/
|
||||
public static void setField(Object target, String fieldName, Object value) {
|
||||
public static <T> T newInstance(String className) {
|
||||
try {
|
||||
Field field = getField(target.getClass(), fieldName);
|
||||
field.set( target, value );
|
||||
return (T) Class.forName( className ).newInstance();
|
||||
}
|
||||
catch ( IllegalAccessException e ) {
|
||||
throw new IllegalArgumentException("Field " + fieldName + " could not be set", e );
|
||||
catch ( ClassNotFoundException | IllegalAccessException | InstantiationException e ) {
|
||||
throw new IllegalArgumentException("Constructor could not be called", e );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get setter method
|
||||
*
|
||||
* @param target target object
|
||||
* @param property property
|
||||
* @param parameterType setter parameter type
|
||||
* @return setter method
|
||||
*/
|
||||
public static Method getSetter(Object target, String property, Class<?> parameterType) {
|
||||
String setterMethodName = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);
|
||||
Method setter = getMethod(target, setterMethodName, parameterType);
|
||||
setter.setAccessible(true);
|
||||
return setter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get target method
|
||||
*
|
||||
* @param target target object
|
||||
* @param methodName method name
|
||||
* @param parameterTypes method parameter types
|
||||
* @return return value
|
||||
*/
|
||||
public static Method getMethod(Object target, String methodName, Class... parameterTypes) {
|
||||
try {
|
||||
return target.getClass().getMethod(methodName, parameterTypes);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke setter method with the given parameter
|
||||
*
|
||||
* @param target target object
|
||||
* @param property property
|
||||
* @param parameter setter parameter
|
||||
*/
|
||||
public static void setProperty(Object target, String property, Object parameter) {
|
||||
Method setter = getSetter( target, property, parameter.getClass());
|
||||
try {
|
||||
setter.invoke(target, parameter);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,10 +39,22 @@ public class PreparedStatementSpyConnectionProvider
|
|||
private final List<String> executeUpdateStatements = new ArrayList<>();
|
||||
|
||||
private final List<Connection> acquiredConnections = new ArrayList<>( );
|
||||
private final List<Connection> releasedConnections = new ArrayList<>( );
|
||||
|
||||
public PreparedStatementSpyConnectionProvider() {
|
||||
}
|
||||
|
||||
public PreparedStatementSpyConnectionProvider(ConnectionProvider connectionProvider) {
|
||||
super( connectionProvider );
|
||||
}
|
||||
|
||||
protected Connection actualConnection() throws SQLException {
|
||||
return super.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
Connection connection = spy( super.getConnection() );
|
||||
Connection connection = spy( actualConnection() );
|
||||
acquiredConnections.add( connection );
|
||||
return connection;
|
||||
}
|
||||
|
@ -50,9 +62,16 @@ public class PreparedStatementSpyConnectionProvider
|
|||
@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;
|
||||
|
@ -94,6 +113,7 @@ public class PreparedStatementSpyConnectionProvider
|
|||
*/
|
||||
public void clear() {
|
||||
acquiredConnections.clear();
|
||||
releasedConnections.clear();
|
||||
preparedStatementMap.keySet().forEach( Mockito::reset );
|
||||
preparedStatementMap.clear();
|
||||
}
|
||||
|
@ -167,4 +187,12 @@ public class PreparedStatementSpyConnectionProvider
|
|||
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,120 @@
|
|||
/*
|
||||
* 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.resource.transaction.jdbc;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
|
||||
import org.hibernate.testing.jdbc.PreparedStatementSpyConnectionProvider;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
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 HikariCPSkipAutoCommitTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
private PreparedStatementSpyConnectionProvider connectionProvider =
|
||||
new PreparedStatementSpyConnectionProvider();
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.getProperties().put(
|
||||
org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER,
|
||||
connectionProvider
|
||||
);
|
||||
configuration.getProperties().put( AvailableSettings.SKIP_AUTOCOMMIT_CHECK, Boolean.TRUE );
|
||||
configuration.getProperties().put( "hibernate.hikari.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,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
|
|
@ -35,6 +35,13 @@ public class ConnectionProviderDelegate implements
|
|||
|
||||
private ConnectionProvider connectionProvider;
|
||||
|
||||
public ConnectionProviderDelegate() {
|
||||
}
|
||||
|
||||
public ConnectionProviderDelegate(ConnectionProvider connectionProvider) {
|
||||
this.connectionProvider = connectionProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
|
@ -42,6 +49,7 @@ public class ConnectionProviderDelegate implements
|
|||
|
||||
@Override
|
||||
public void configure(Map configurationValues) {
|
||||
if ( connectionProvider == null ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> settings = new HashMap<>( configurationValues );
|
||||
settings.remove( AvailableSettings.CONNECTION_PROVIDER );
|
||||
|
@ -54,6 +62,7 @@ public class ConnectionProviderDelegate implements
|
|||
configurableConnectionProvider.configure( settings );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
|
|
Loading…
Reference in New Issue