Add support for advanced customization of Hibernate settings
As HibernateJpaConfiguration is package private, it is no longer possible to extend the default Spring Boot configuration to apply advanced settings. The most notable use case for this is the customization of Hibernate properties using instance value vs. string value that can be set using the "spring.jpa.properties" namespace. This commit adds a HibernatePropertiesCustomizer callback interface that can be implemented to tune Hibernate properties at will. Closes gh-11211
This commit is contained in:
parent
268b97bf98
commit
59d5ed5842
|
|
@ -87,13 +87,16 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
|
|||
|
||||
private final ImplicitNamingStrategy implicitNamingStrategy;
|
||||
|
||||
private final List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers;
|
||||
|
||||
HibernateJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties,
|
||||
ObjectProvider<JtaTransactionManager> jtaTransactionManager,
|
||||
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers,
|
||||
ObjectProvider<Collection<DataSourcePoolMetadataProvider>> metadataProviders,
|
||||
ObjectProvider<List<SchemaManagementProvider>> providers,
|
||||
ObjectProvider<PhysicalNamingStrategy> physicalNamingStrategy,
|
||||
ObjectProvider<ImplicitNamingStrategy> implicitNamingStrategy) {
|
||||
ObjectProvider<ImplicitNamingStrategy> implicitNamingStrategy,
|
||||
ObjectProvider<List<HibernatePropertiesCustomizer>> hibernatePropertiesCustomizers) {
|
||||
super(dataSource, jpaProperties, jtaTransactionManager,
|
||||
transactionManagerCustomizers);
|
||||
this.defaultDdlAutoProvider = new HibernateDefaultDdlAutoProvider(
|
||||
|
|
@ -102,6 +105,8 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
|
|||
metadataProviders.getIfAvailable());
|
||||
this.physicalNamingStrategy = physicalNamingStrategy.getIfAvailable();
|
||||
this.implicitNamingStrategy = implicitNamingStrategy.getIfAvailable();
|
||||
this.hibernatePropertiesCustomizers = hibernatePropertiesCustomizers
|
||||
.getIfAvailable(() -> Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -111,14 +116,13 @@ class HibernateJpaConfiguration extends JpaBaseConfiguration {
|
|||
|
||||
@Override
|
||||
protected Map<String, Object> getVendorProperties() {
|
||||
Map<String, Object> vendorProperties = new LinkedHashMap<>();
|
||||
String defaultDdlMode = this.defaultDdlAutoProvider
|
||||
.getDefaultDdlAuto(getDataSource());
|
||||
vendorProperties.putAll(getProperties()
|
||||
return new LinkedHashMap<>(getProperties()
|
||||
.getHibernateProperties(new HibernateSettings().ddlAuto(defaultDdlMode)
|
||||
.implicitNamingStrategy(this.implicitNamingStrategy)
|
||||
.physicalNamingStrategy(this.physicalNamingStrategy)));
|
||||
return vendorProperties;
|
||||
.physicalNamingStrategy(this.physicalNamingStrategy)
|
||||
.hibernatePropertiesCustomizers(this.hibernatePropertiesCustomizers)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.orm.jpa;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize the Hibernate
|
||||
* properties before it is used by an auto-configured {@code EntityManagerFactory}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface HibernatePropertiesCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the specified JPA vendor properties.
|
||||
* @param hibernateProperties the current JPA vendor properties
|
||||
*/
|
||||
void customize(Map<String, Object> hibernateProperties);
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.orm.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
|
||||
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
|
||||
|
||||
|
|
@ -33,6 +36,8 @@ public class HibernateSettings {
|
|||
|
||||
private PhysicalNamingStrategy physicalNamingStrategy;
|
||||
|
||||
private Collection<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers;
|
||||
|
||||
public HibernateSettings ddlAuto(String ddlAuto) {
|
||||
this.ddlAuto = ddlAuto;
|
||||
return this;
|
||||
|
|
@ -62,4 +67,15 @@ public class HibernateSettings {
|
|||
return this.physicalNamingStrategy;
|
||||
}
|
||||
|
||||
public HibernateSettings hibernatePropertiesCustomizers(
|
||||
Collection<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers) {
|
||||
this.hibernatePropertiesCustomizers = new ArrayList<>();
|
||||
this.hibernatePropertiesCustomizers.addAll(hibernatePropertiesCustomizers);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Collection<HibernatePropertiesCustomizer> getHibernatePropertiesCustomizers() {
|
||||
return this.hibernatePropertiesCustomizers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.autoconfigure.orm.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -28,6 +29,7 @@ import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
|
|||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -222,6 +224,12 @@ public class JpaProperties {
|
|||
else {
|
||||
result.remove("hibernate.hbm2ddl.auto");
|
||||
}
|
||||
Collection<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers =
|
||||
settings.getHibernatePropertiesCustomizers();
|
||||
if (!ObjectUtils.isEmpty(hibernatePropertiesCustomizers)) {
|
||||
hibernatePropertiesCustomizers.forEach((customizer)
|
||||
-> customizer.customize(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.orm.jpa.test.City;
|
|||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
|
@ -55,6 +56,7 @@ import static org.mockito.Mockito.mock;
|
|||
public class CustomHibernateJpaAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.datasource.generate-unique-name=true")
|
||||
.withUserConfiguration(TestConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class));
|
||||
|
|
@ -86,10 +88,23 @@ public class CustomHibernateJpaAutoConfigurationTests {
|
|||
.getVendorProperties();
|
||||
assertThat(hibernateProperties
|
||||
.get("hibernate.implicit_naming_strategy")).isEqualTo(
|
||||
NamingStrategyConfiguration.implicitNamingStrategy);
|
||||
NamingStrategyConfiguration.implicitNamingStrategy);
|
||||
assertThat(hibernateProperties
|
||||
.get("hibernate.physical_naming_strategy")).isEqualTo(
|
||||
NamingStrategyConfiguration.physicalNamingStrategy);
|
||||
NamingStrategyConfiguration.physicalNamingStrategy);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hibernatePropertiesCustomizersAreAppliedInOrder() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(HibernatePropertiesCustomizerConfiguration.class)
|
||||
.run((context) -> {
|
||||
HibernateJpaConfiguration jpaConfiguration = context
|
||||
.getBean(HibernateJpaConfiguration.class);
|
||||
Map<String, Object> hibernateProperties = jpaConfiguration
|
||||
.getVendorProperties();
|
||||
assertThat(hibernateProperties.get("test.counter")).isEqualTo(2);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -149,4 +164,21 @@ public class CustomHibernateJpaAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class HibernatePropertiesCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(2)
|
||||
public HibernatePropertiesCustomizer sampleCustomizer() {
|
||||
return ((hibernateProperties) -> hibernateProperties.put("test.counter", 2));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
public HibernatePropertiesCustomizer anotherCustomizer() {
|
||||
return ((hibernateProperties) -> hibernateProperties.put("test.counter", 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.orm.jpa;
|
|||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
|
@ -126,6 +127,40 @@ public class JpaPropertiesTests {
|
|||
}));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void hibernatePropertiesCustomizerTakePrecedenceOverStrategyInstancesAndNamingStrategyProperties() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.jpa.hibernate.naming.implicit-strategy:com.example.Implicit",
|
||||
"spring.jpa.hibernate.naming.physical-strategy:com.example.Physical"
|
||||
).run(assertJpaProperties((properties) -> {
|
||||
ImplicitNamingStrategy implicitStrategy = mock(ImplicitNamingStrategy.class);
|
||||
PhysicalNamingStrategy physicalStrategy = mock(PhysicalNamingStrategy.class);
|
||||
ImplicitNamingStrategy effectiveImplicitStrategy = mock(
|
||||
ImplicitNamingStrategy.class);
|
||||
PhysicalNamingStrategy effectivePhysicalStrategy = mock(
|
||||
PhysicalNamingStrategy.class);
|
||||
HibernatePropertiesCustomizer customizer = (hibernateProperties) -> {
|
||||
hibernateProperties.put("hibernate.implicit_naming_strategy",
|
||||
effectiveImplicitStrategy);
|
||||
hibernateProperties.put("hibernate.physical_naming_strategy",
|
||||
effectivePhysicalStrategy);
|
||||
};
|
||||
Map<String, Object> hibernateProperties = properties
|
||||
.getHibernateProperties(new HibernateSettings().ddlAuto("none")
|
||||
.implicitNamingStrategy(implicitStrategy)
|
||||
.physicalNamingStrategy(physicalStrategy)
|
||||
.hibernatePropertiesCustomizers(
|
||||
Collections.singleton(customizer)));
|
||||
assertThat(hibernateProperties).contains(
|
||||
entry("hibernate.implicit_naming_strategy", effectiveImplicitStrategy),
|
||||
entry("hibernate.physical_naming_strategy", effectivePhysicalStrategy));
|
||||
assertThat(hibernateProperties)
|
||||
.doesNotContainKeys("hibernate.ejb.naming_strategy");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hibernate5CustomNamingStrategiesViaJpaProperties() {
|
||||
this.contextRunner
|
||||
|
|
|
|||
|
|
@ -1787,6 +1787,11 @@ In addition, all properties in `+spring.jpa.properties.*+` are passed through as
|
|||
JPA properties (with the prefix stripped) when the local `EntityManagerFactory` is
|
||||
created.
|
||||
|
||||
TIP: if you need to apply advanced customization to Hibernate properties, consider
|
||||
registering a `HibernatePropertiesCustomizer` bean that will be invoked prior to creating
|
||||
the `EntityManagerFactory`. This takes precedence to anything that is applied by the
|
||||
auto-configuration.
|
||||
|
||||
|
||||
|
||||
[[howto-configure-hibernate-naming-strategy]]
|
||||
|
|
|
|||
Loading…
Reference in New Issue