Merge pull request #9737 from candrews:autocommit

* pr/9737:
  Polish "When pool autocommit is disabled, inform Hibernate"
  When pool autocommit is disabled, inform Hibernate
pull/10658/merge
Stephane Nicoll 7 years ago
commit 4c5c5105b3

@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.orm.jpa; package org.springframework.boot.autoconfigure.orm.jpa;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@ -30,6 +31,9 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.jdbc.SchemaManagementProvider; import org.springframework.boot.jdbc.SchemaManagementProvider;
import org.springframework.boot.jdbc.metadata.CompositeDataSourcePoolMetadataProvider;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform; import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.jndi.JndiLocatorDelegate; import org.springframework.jndi.JndiLocatorDelegate;
@ -56,6 +60,8 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
private static final String JTA_PLATFORM = "hibernate.transaction.jta.platform"; private static final String JTA_PLATFORM = "hibernate.transaction.jta.platform";
private static final String PROVIDER_DISABLES_AUTOCOMMIT = "hibernate.connection.provider_disables_autocommit";
/** /**
* {@code NoJtaPlatform} implementations for various Hibernate versions. * {@code NoJtaPlatform} implementations for various Hibernate versions.
*/ */
@ -73,14 +79,19 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
private final HibernateDefaultDdlAutoProvider defaultDdlAutoProvider; private final HibernateDefaultDdlAutoProvider defaultDdlAutoProvider;
private DataSourcePoolMetadataProvider poolMetadataProvider;
HibernateJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties, HibernateJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager, ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers,
ObjectProvider<Collection<DataSourcePoolMetadataProvider>> metadataProviders,
ObjectProvider<List<SchemaManagementProvider>> providers) { ObjectProvider<List<SchemaManagementProvider>> providers) {
super(dataSource, jpaProperties, jtaTransactionManager, super(dataSource, jpaProperties, jtaTransactionManager,
transactionManagerCustomizers); transactionManagerCustomizers);
this.defaultDdlAutoProvider = new HibernateDefaultDdlAutoProvider( this.defaultDdlAutoProvider = new HibernateDefaultDdlAutoProvider(
providers.getIfAvailable(Collections::emptyList)); providers.getIfAvailable(Collections::emptyList));
this.poolMetadataProvider = new CompositeDataSourcePoolMetadataProvider(
metadataProviders.getIfAvailable());
} }
@Override @Override
@ -103,6 +114,9 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
if (!vendorProperties.containsKey(JTA_PLATFORM)) { if (!vendorProperties.containsKey(JTA_PLATFORM)) {
configureJtaPlatform(vendorProperties); configureJtaPlatform(vendorProperties);
} }
if (!vendorProperties.containsKey(PROVIDER_DISABLES_AUTOCOMMIT)) {
configureProviderDisablesAutocommit(vendorProperties);
}
} }
private void configureJtaPlatform(Map<String, Object> vendorProperties) private void configureJtaPlatform(Map<String, Object> vendorProperties)
@ -124,6 +138,19 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
} }
} }
private void configureProviderDisablesAutocommit(Map<String, Object> vendorProperties) {
if (isDataSourceAutoCommitDisabled() && !isJta()) {
vendorProperties.put(PROVIDER_DISABLES_AUTOCOMMIT, "true");
}
}
private boolean isDataSourceAutoCommitDisabled() {
DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider
.getDataSourcePoolMetadata(getDataSource());
return poolMetadata != null
&& Boolean.FALSE.equals(poolMetadata.getDefaultAutoCommit());
}
private boolean runningOnWebSphere() { private boolean runningOnWebSphere() {
return ClassUtils.isPresent( return ClassUtils.isPresent(
"com.ibm.websphere.jtaextensions." + "ExtendedJTATransaction", "com.ibm.websphere.jtaextensions." + "ExtendedJTATransaction",

@ -33,6 +33,7 @@ import javax.transaction.Transaction;
import javax.transaction.TransactionManager; import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction; import javax.transaction.UserTransaction;
import com.zaxxer.hikari.HikariDataSource;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.junit.Test; import org.junit.Test;
@ -52,6 +53,7 @@ import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
@ -75,12 +77,12 @@ public class HibernateJpaAutoConfigurationTests
contextRunner().withPropertyValues("spring.datasource.data:classpath:/city.sql", contextRunner().withPropertyValues("spring.datasource.data:classpath:/city.sql",
// Missing: // Missing:
"spring.datasource.schema:classpath:/ddl.sql").run((context) -> { "spring.datasource.schema:classpath:/ddl.sql").run((context) -> {
assertThat(context).hasFailed(); assertThat(context).hasFailed();
assertThat(context.getStartupFailure()) assertThat(context.getStartupFailure())
.hasMessageContaining("ddl.sql"); .hasMessageContaining("ddl.sql");
assertThat(context.getStartupFailure()) assertThat(context.getStartupFailure())
.hasMessageContaining("spring.datasource.schema"); .hasMessageContaining("spring.datasource.schema");
}); });
} }
@Test @Test
@ -105,7 +107,7 @@ public class HibernateJpaAutoConfigurationTests
"spring.datasource.data:classpath:/city.sql") "spring.datasource.data:classpath:/city.sql")
.run((context) -> assertThat( .run((context) -> assertThat(
context.getBean(TestInitializedJpaConfiguration.class).called) context.getBean(TestInitializedJpaConfiguration.class).called)
.isTrue()); .isTrue());
} }
@Test @Test
@ -164,7 +166,7 @@ public class HibernateJpaAutoConfigurationTests
.getJpaPropertyMap(); .getJpaPropertyMap();
assertThat((String) jpaPropertyMap assertThat((String) jpaPropertyMap
.get("hibernate.transaction.jta.platform")) .get("hibernate.transaction.jta.platform"))
.isEqualTo(TestJtaPlatform.class.getName()); .isEqualTo(TestJtaPlatform.class.getName());
}); });
} }
@ -194,6 +196,62 @@ public class HibernateJpaAutoConfigurationTests
}); });
} }
@Test
public void providerDisablesAutoCommitIsConfigured() {
contextRunner().withPropertyValues(
"spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.auto-commit:false").run((context) -> {
Map<String, Object> jpaProperties = context
.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).contains(entry(
"hibernate.connection.provider_disables_autocommit", "true"));
});
}
@Test
public void providerDisablesAutoCommitIsNotConfiguredIfAutoCommitIsEnabled() {
contextRunner().withPropertyValues(
"spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.auto-commit:true").run((context) -> {
Map<String, Object> jpaProperties = context
.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).doesNotContainKeys(
"hibernate.connection.provider_disables_autocommit");
});
}
@Test
public void providerDisablesAutoCommitIsNotConfiguredIfPropertyIsSet() {
contextRunner().withPropertyValues(
"spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.auto-commit:false",
"spring.jpa.properties.hibernate.connection.provider_disables_autocommit=false"
).run((context) -> {
Map<String, Object> jpaProperties = context
.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).contains(entry(
"hibernate.connection.provider_disables_autocommit", "false"));
});
}
@Test
public void providerDisablesAutoCommitIsNotConfiguredWihJta() {
contextRunner()
.withConfiguration(AutoConfigurations.of(JtaAutoConfiguration.class))
.withPropertyValues(
"spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.auto-commit:false").run((context) -> {
Map<String, Object> jpaProperties = context
.getBean(LocalContainerEntityManagerFactoryBean.class)
.getJpaPropertyMap();
assertThat(jpaProperties).doesNotContainKeys(
"hibernate.connection.provider_disables_autocommit");
});
}
@Configuration @Configuration
@TestAutoConfigurationPackage(City.class) @TestAutoConfigurationPackage(City.class)
static class TestInitializedJpaConfiguration { static class TestInitializedJpaConfiguration {

@ -53,4 +53,9 @@ public class CommonsDbcp2DataSourcePoolMetadata
return getDataSource().getValidationQuery(); return getDataSource().getValidationQuery();
} }
@Override
public Boolean getDefaultAutoCommit() {
return getDataSource().getDefaultAutoCommit();
}
} }

@ -71,4 +71,12 @@ public interface DataSourcePoolMetadata {
*/ */
String getValidationQuery(); String getValidationQuery();
/**
* The default auto-commit state of connections created by this pool.
* If not set ({@code null}), default is JDBC driver default
* (If set to null then the java.sql.Connection.setAutoCommit(boolean) method will not be called.)
* @return the default auto-commit state or {@code null}
*/
Boolean getDefaultAutoCommit();
} }

@ -66,4 +66,9 @@ public class HikariDataSourcePoolMetadata
return getDataSource().getConnectionTestQuery(); return getDataSource().getConnectionTestQuery();
} }
@Override
public Boolean getDefaultAutoCommit() {
return getDataSource().isAutoCommit();
}
} }

@ -53,4 +53,9 @@ public class TomcatDataSourcePoolMetadata
return getDataSource().getValidationQuery(); return getDataSource().getValidationQuery();
} }
@Override
public Boolean getDefaultAutoCommit() {
return getDataSource().isDefaultAutoCommit();
}
} }

@ -86,6 +86,9 @@ public abstract class AbstractDataSourcePoolMetadataTests<D extends AbstractData
@Test @Test
public abstract void getValidationQuery(); public abstract void getValidationQuery();
@Test
public abstract void getDefaultAutoCommit();
protected DataSourceBuilder<?> initializeBuilder() { protected DataSourceBuilder<?> initializeBuilder() {
return DataSourceBuilder.create().driverClassName("org.hsqldb.jdbc.JDBCDriver") return DataSourceBuilder.create().driverClassName("org.hsqldb.jdbc.JDBCDriver")
.url("jdbc:hsqldb:mem:test").username("sa"); .url("jdbc:hsqldb:mem:test").username("sa");

@ -81,6 +81,14 @@ public class CommonsDbcp2DataSourcePoolMetadataTests
.isEqualTo("SELECT FROM FOO"); .isEqualTo("SELECT FROM FOO");
} }
@Override
public void getDefaultAutoCommit() {
BasicDataSource dataSource = createDataSource();
dataSource.setDefaultAutoCommit(false);
assertThat(new CommonsDbcp2DataSourcePoolMetadata(dataSource)
.getDefaultAutoCommit()).isFalse();
}
private CommonsDbcp2DataSourcePoolMetadata createDataSourceMetadata(int minSize, private CommonsDbcp2DataSourcePoolMetadata createDataSourceMetadata(int minSize,
int maxSize) { int maxSize) {
BasicDataSource dataSource = createDataSource(); BasicDataSource dataSource = createDataSource();

@ -50,6 +50,14 @@ public class HikariDataSourcePoolMetadataTests
.isEqualTo("SELECT FROM FOO"); .isEqualTo("SELECT FROM FOO");
} }
@Override
public void getDefaultAutoCommit() {
HikariDataSource dataSource = createDataSource(0, 4);
dataSource.setAutoCommit(false);
assertThat(new HikariDataSourcePoolMetadata(dataSource).getDefaultAutoCommit())
.isFalse();
}
private HikariDataSource createDataSource(int minSize, int maxSize) { private HikariDataSource createDataSource(int minSize, int maxSize) {
HikariDataSource dataSource = initializeBuilder().type(HikariDataSource.class) HikariDataSource dataSource = initializeBuilder().type(HikariDataSource.class)
.build(); .build();

@ -50,6 +50,14 @@ public class TomcatDataSourcePoolMetadataTests
.isEqualTo("SELECT FROM FOO"); .isEqualTo("SELECT FROM FOO");
} }
@Override
public void getDefaultAutoCommit() {
DataSource dataSource = createDataSource(0, 4);
dataSource.setDefaultAutoCommit(false);
assertThat(new TomcatDataSourcePoolMetadata(dataSource).getDefaultAutoCommit())
.isFalse();
}
private DataSource createDataSource(int minSize, int maxSize) { private DataSource createDataSource(int minSize, int maxSize) {
DataSource dataSource = initializeBuilder().type(DataSource.class).build(); DataSource dataSource = initializeBuilder().type(DataSource.class).build();
dataSource.setMinIdle(minSize); dataSource.setMinIdle(minSize);

Loading…
Cancel
Save