使用 Spring Boot 2.1+ 配置 Hibernate 缓存
Configuring caching for Hibernate with Spring Boot 2.1+
上下文和问题
我正在尝试在 Spring Boot 2.2 中使用 Hibernate 配置 EHCache,但似乎我做错了什么。
我看过几个教程和 SO 问题,但没有找到完全符合我的方法的内容。
我选择了一个没有-XML的方法,用于缓存的jcache配置。
但是,Hibernate 没有检测到现有的缓存管理器(我检查甚至使用 @AutoconfigureBefore
强制执行:缓存管理器在 Hibernate 自动配置 之前 加载)。
结果,Hibernate 创建了第二个 EhcacheManager
并抛出几个警告,如下所示:
HHH90001006: Missing cache[com.example.demo.one.dto.MyModel] was created on-the-fly. The created cache will use a provider-specific default configuration: make sure you defined one. You can disable this warning by setting 'hibernate.javax.cache.missing_cache_strategy' to 'create'.
我尝试使用 HibernatePropertiesCustomizer
来告诉 Hibernate 它应该使用哪个缓存管理器。
该 bean 已实例化,但从未被调用,因此它失去了所有吸引力和用途。
有人知道我做错了什么以及我应该如何让 Hibernate 使用我已经配置的缓存管理器而不是创建他自己的缓存管理器吗?
我将我的配置与 JHipster 生成的配置进行了比较。
它看起来非常相似,尽管它们的 HibernatePropertiesCustomizer
是 调用的。
我没有成功识别他们的缓存配置和我的之间的区别。
后来测试的笔记(编辑)
这似乎与我的数据源配置有关(请参阅下面的代码)。
我尝试删除它并以更简单的方式启用我的 JPA 配置,并且确实按预期调用了 HibernatePropertiesCustomizer
。
@SpringBootApplication
@EnableTransactionManagement
@EnableJpaRepositories("com.example.demo.one.repository")
public class DemoApplication {
实际上,手动配置了我的数据源(因为我需要处理两个不同的数据源),我绕过了 Spring Boot 的 DataSourceAutoConfiguration
,而 它的 HibernateJpaAutoConfiguration
是未应用。
此自动配置是应用 HibernatePropertiesCustomizer
的自动配置(相反,它调用 HibernateJpaConfiguration
来执行此操作)。
但是,我不确定应该如何调用此配置来应用它。
代码示例
依赖关系
我使用以下依赖项(我让 spring-boot-starter-parent
设置版本):
- org.springframework.boot:spring-boot-starter-data-jpa
- org.springframework.boot:spring-boot-starter-cache
- org.hibernate:hibernate-jcache
- javax.cache:缓存-api
- org.ehcache:ehcache
- org.projectlombok:龙目岛作为一种安慰
缓存配置
package com.example.demo.config;
import lombok.extern.slf4j.Slf4j;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.cache.CacheManager;
import java.time.Duration;
@Configuration
@EnableCaching
@Slf4j
//@AutoConfigureBefore(value = {DataSource1Config.class, DataSource2Config.class})
public class CacheConfiguration {
private static final int TIME_TO_LIVE_SECONDS = 240;
private static final int MAX_ELEMENTS_DEFAULT = 200;
// Create this configuration as a bean so that it is used to customize automatically created caches
@Bean
public javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration() {
final org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration =
CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(MAX_ELEMENTS_DEFAULT))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(TIME_TO_LIVE_SECONDS)))
.build();
return Eh107Configuration.fromEhcacheCacheConfiguration(
cacheConfiguration
);
}
@Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(javax.cache.CacheManager cacheManager) {
log.error(">>>>>>>>>>>> customizer setup"); // Printed
return hibernateProperties -> {
log.error(">>>>>>>>>>>> customizer called"); // Not printed
hibernateProperties.put("hibernate.javax.cache.cache_manager", cacheManager);
};
}
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer(javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration) {
return cm -> {
createCache(cm, com.example.demo.one.dto.MyModel.class.getName(), jcacheConfiguration);
};
}
private void createCache(CacheManager cm, String cacheName, javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration) {
javax.cache.Cache<Object, Object> cache = cm.getCache(cacheName);
if (cache != null) {
cm.destroyCache(cacheName);
}
cm.createCache(cacheName, jcacheConfiguration);
}
}
数据源配置
我有两个数据源。
第二个与这个类似,减去 @Primary
注释。
删除第二个数据源并不能解决问题。
package com.example.demo.config;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.example.demo.one.repository",
entityManagerFactoryRef = "dataSource1EntityManagerFactory",
transactionManagerRef = "transactionManager1"
)
public class DataSource1Config {
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.one")
public DataSourceProperties dataSource1Properties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource dataSource1(DataSourceProperties dataSource1Properties) {
return dataSource1Properties.initializeDataSourceBuilder().build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource1) {
return builder
.dataSource(dataSource1)
.packages("com.example.demo.one.dto")
.build();
}
@Bean
@Primary
public PlatformTransactionManager transactionManager1(EntityManagerFactory dataSource1EntityManagerFactory) {
return new JpaTransactionManager(dataSource1EntityManagerFactory);
}
}
application.yml
spring:
jpa:
database: <my-db>
hibernate:
ddl-auto: validate
properties:
hibernate:
dialect: <my-dialect>
jdbc.time_zone: UTC
javax:
cache:
#missing_cache_strategy: fail # Useful for testing if Hibernate creates a second cache manager
cache:
use_second_level_cache: true
use_query_cache: false
region.factory_class: jcache
这并不容易,但我找到了原因和解决方案。
原因
基本上,问题出在我自己配置LocalContainerEntityManagerFactoryBean
。
如果您不这样做,Spring Boot 将使用它的自动配置来创建一切都很好,包括供应商属性(您在 spring.jpa.properties
下拥有的所有内容)、休眠属性(在 spring.jpa.hibernate
),并应用默认设置和自定义设置,其中我寻找已久的 HibernateJpaAutoConfiguration
.
但是因为我需要有多个数据源,所以我绕过了所有这些,听了我的教程,我做了懒惰的跟随。
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource1) {
return builder
.dataSource(dataSource1)
.packages("com.example.demo.one.dto")
.build();
}
解决方案
简而言之
解决方案几乎很简单:做所有事情 Spring Boot 会做的。
仅 "Almost",因为这些机制中的大多数都依赖于自动配置(覆盖这些是一种代码味道,所以这不是实现它的方法)and/or internal/protected classes(这你不能直接打电话)。
可能会变脆?
这意味着您基本上必须将 Spring Boot 的代码复制到您自己的代码中,可能会对 Spring Boot 的未来升级造成一些脆弱性(或者只是您的代码不会受益于最新的 bug/performances 修复)。
从这方面来说,我不太喜欢我在这里提出的解决方案。
详细指南
你依赖的豆子
您需要将以下 bean 注入到您的数据源配置中:
org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties
org.springframework.boot.autoconfigure.orm.jpa.JpaProperties
List<org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer>
要执行的操作
借鉴 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration
,我添加了 hibernate.resource.beans.container
属性 定制器。
但是,我跳过了在我们的项目中不是问题的命名策略。
这为我提供了以下构造函数和方法:
public DataSource1Config(
JpaProperties jpaProperties,
HibernateProperties hibernateProperties,
ConfigurableListableBeanFactory beanFactory,
ObjectProvider<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers
) {
this.jpaProperties = jpaProperties;
this.hibernateProperties = hibernateProperties;
this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers(
beanFactory,
hibernatePropertiesCustomizers.orderedStream().collect(Collectors.toList())
);
}
private List<HibernatePropertiesCustomizer> determineHibernatePropertiesCustomizers(
ConfigurableListableBeanFactory beanFactory,
List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers
) {
List<HibernatePropertiesCustomizer> customizers = new ArrayList<>();
if (ClassUtils.isPresent("org.hibernate.resource.beans.container.spi.BeanContainer",
getClass().getClassLoader())) {
customizers.add((properties) -> properties.put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory)));
}
customizers.addAll(hibernatePropertiesCustomizers);
return customizers;
}
然后,根据 org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration
,我加载了供应商属性。
在这里,我再次跳过了一些您可以查看的自动自定义(JpaBaseConfiguration#customizeVendorProperties(Map)
及其在 subclasses 中的实现)。
private Map<String, Object> getVendorProperties() {
return new LinkedHashMap<>(
this.hibernateProperties
.determineHibernateProperties(jpaProperties.getProperties(),
new HibernateSettings()
// Spring Boot's HibernateDefaultDdlAutoProvider is not available here
.hibernatePropertiesCustomizers(this.hibernatePropertiesCustomizers)
)
);
}
完成配置class
作为参考,我在应用上述详细更改后向您提供我的完整配置 class。
package com.example.demo.config;
import org.hibernate.cfg.AvailableSettings;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.hibernate5.SpringBeanContainer;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.ClassUtils;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.example.demo.one.repository",
entityManagerFactoryRef = "dataSource1EntityManagerFactory",
transactionManagerRef = "TransactionManager1"
)
public class DataSource1Config {
private final JpaProperties jpaProperties;
private final HibernateProperties hibernateProperties;
private final List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers;
public DataSource1Config(
JpaProperties jpaProperties,
HibernateProperties hibernateProperties,
ConfigurableListableBeanFactory beanFactory,
ObjectProvider<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers
) {
this.jpaProperties = jpaProperties;
this.hibernateProperties = hibernateProperties;
this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers(
beanFactory,
hibernatePropertiesCustomizers.orderedStream().collect(Collectors.toList())
);
}
private List<HibernatePropertiesCustomizer> determineHibernatePropertiesCustomizers(
ConfigurableListableBeanFactory beanFactory,
List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers
) {
List<HibernatePropertiesCustomizer> customizers = new ArrayList<>();
if (ClassUtils.isPresent("org.hibernate.resource.beans.container.spi.BeanContainer",
getClass().getClassLoader())) {
customizers.add((properties) -> properties.put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory)));
}
customizers.addAll(hibernatePropertiesCustomizers);
return customizers;
}
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.lib")
public DataSourceProperties dataSource1Properties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource dataSource1(DataSourceProperties dataSource1Properties) {
return dataSource1Properties.initializeDataSourceBuilder().build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(EntityManagerFactoryBuilder factoryBuilder, DataSource dataSource1) {
final Map<String, Object> vendorProperties = getVendorProperties();
return factoryBuilder
.dataSource(dataSource1)
.packages("com.example.demo.one.dto")
.properties(vendorProperties)
.build();
}
@Bean
@Primary
public PlatformTransactionManager transactionManager1(EntityManagerFactory dataSource1EntityManagerFactory) {
return new JpaTransactionManager(dataSource1EntityManagerFactory);
}
private Map<String, Object> getVendorProperties() {
return new LinkedHashMap<>(
this.hibernateProperties
.determineHibernateProperties(jpaProperties.getProperties(),
new HibernateSettings()
// Spring Boot's HibernateDefaultDdlAutoProvider is not available here
.hibernatePropertiesCustomizers(this.hibernatePropertiesCustomizers)
)
);
}
}
您可以简单地将 cacheManager
注入并放入您的 dataSource1EntityManagerFactory
中。无需使用 HibernatePropertiesCustomizer
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(
EntityManagerFactoryBuilder builder,
DataSource dataSource1,
javax.cache.CacheManager cacheManager) {
return builder
.dataSource(dataSource1)
.properties(Map.of(org.hibernate.cache.jcache.ConfigSettings.CACHE_MANAGER, cacheManager))
.packages("com.example.demo.one.dto")
.build();
}
上下文和问题
我正在尝试在 Spring Boot 2.2 中使用 Hibernate 配置 EHCache,但似乎我做错了什么。 我看过几个教程和 SO 问题,但没有找到完全符合我的方法的内容。
我选择了一个没有-XML的方法,用于缓存的jcache配置。
但是,Hibernate 没有检测到现有的缓存管理器(我检查甚至使用 @AutoconfigureBefore
强制执行:缓存管理器在 Hibernate 自动配置 之前 加载)。
结果,Hibernate 创建了第二个 EhcacheManager
并抛出几个警告,如下所示:
HHH90001006: Missing cache[com.example.demo.one.dto.MyModel] was created on-the-fly. The created cache will use a provider-specific default configuration: make sure you defined one. You can disable this warning by setting 'hibernate.javax.cache.missing_cache_strategy' to 'create'.
我尝试使用 HibernatePropertiesCustomizer
来告诉 Hibernate 它应该使用哪个缓存管理器。
该 bean 已实例化,但从未被调用,因此它失去了所有吸引力和用途。
有人知道我做错了什么以及我应该如何让 Hibernate 使用我已经配置的缓存管理器而不是创建他自己的缓存管理器吗?
我将我的配置与 JHipster 生成的配置进行了比较。
它看起来非常相似,尽管它们的 HibernatePropertiesCustomizer
是 调用的。
我没有成功识别他们的缓存配置和我的之间的区别。
后来测试的笔记(编辑)
这似乎与我的数据源配置有关(请参阅下面的代码)。
我尝试删除它并以更简单的方式启用我的 JPA 配置,并且确实按预期调用了 HibernatePropertiesCustomizer
。
@SpringBootApplication
@EnableTransactionManagement
@EnableJpaRepositories("com.example.demo.one.repository")
public class DemoApplication {
实际上,手动配置了我的数据源(因为我需要处理两个不同的数据源),我绕过了 Spring Boot 的 DataSourceAutoConfiguration
,而 它的 HibernateJpaAutoConfiguration
是未应用。
此自动配置是应用 HibernatePropertiesCustomizer
的自动配置(相反,它调用 HibernateJpaConfiguration
来执行此操作)。
但是,我不确定应该如何调用此配置来应用它。
代码示例
依赖关系
我使用以下依赖项(我让 spring-boot-starter-parent
设置版本):
- org.springframework.boot:spring-boot-starter-data-jpa
- org.springframework.boot:spring-boot-starter-cache
- org.hibernate:hibernate-jcache
- javax.cache:缓存-api
- org.ehcache:ehcache
- org.projectlombok:龙目岛作为一种安慰
缓存配置
package com.example.demo.config;
import lombok.extern.slf4j.Slf4j;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.cache.CacheManager;
import java.time.Duration;
@Configuration
@EnableCaching
@Slf4j
//@AutoConfigureBefore(value = {DataSource1Config.class, DataSource2Config.class})
public class CacheConfiguration {
private static final int TIME_TO_LIVE_SECONDS = 240;
private static final int MAX_ELEMENTS_DEFAULT = 200;
// Create this configuration as a bean so that it is used to customize automatically created caches
@Bean
public javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration() {
final org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration =
CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(MAX_ELEMENTS_DEFAULT))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(TIME_TO_LIVE_SECONDS)))
.build();
return Eh107Configuration.fromEhcacheCacheConfiguration(
cacheConfiguration
);
}
@Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(javax.cache.CacheManager cacheManager) {
log.error(">>>>>>>>>>>> customizer setup"); // Printed
return hibernateProperties -> {
log.error(">>>>>>>>>>>> customizer called"); // Not printed
hibernateProperties.put("hibernate.javax.cache.cache_manager", cacheManager);
};
}
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer(javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration) {
return cm -> {
createCache(cm, com.example.demo.one.dto.MyModel.class.getName(), jcacheConfiguration);
};
}
private void createCache(CacheManager cm, String cacheName, javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration) {
javax.cache.Cache<Object, Object> cache = cm.getCache(cacheName);
if (cache != null) {
cm.destroyCache(cacheName);
}
cm.createCache(cacheName, jcacheConfiguration);
}
}
数据源配置
我有两个数据源。
第二个与这个类似,减去 @Primary
注释。
删除第二个数据源并不能解决问题。
package com.example.demo.config;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.example.demo.one.repository",
entityManagerFactoryRef = "dataSource1EntityManagerFactory",
transactionManagerRef = "transactionManager1"
)
public class DataSource1Config {
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.one")
public DataSourceProperties dataSource1Properties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource dataSource1(DataSourceProperties dataSource1Properties) {
return dataSource1Properties.initializeDataSourceBuilder().build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource1) {
return builder
.dataSource(dataSource1)
.packages("com.example.demo.one.dto")
.build();
}
@Bean
@Primary
public PlatformTransactionManager transactionManager1(EntityManagerFactory dataSource1EntityManagerFactory) {
return new JpaTransactionManager(dataSource1EntityManagerFactory);
}
}
application.yml
spring:
jpa:
database: <my-db>
hibernate:
ddl-auto: validate
properties:
hibernate:
dialect: <my-dialect>
jdbc.time_zone: UTC
javax:
cache:
#missing_cache_strategy: fail # Useful for testing if Hibernate creates a second cache manager
cache:
use_second_level_cache: true
use_query_cache: false
region.factory_class: jcache
这并不容易,但我找到了原因和解决方案。
原因
基本上,问题出在我自己配置LocalContainerEntityManagerFactoryBean
。
如果您不这样做,Spring Boot 将使用它的自动配置来创建一切都很好,包括供应商属性(您在 spring.jpa.properties
下拥有的所有内容)、休眠属性(在 spring.jpa.hibernate
),并应用默认设置和自定义设置,其中我寻找已久的 HibernateJpaAutoConfiguration
.
但是因为我需要有多个数据源,所以我绕过了所有这些,听了我的教程,我做了懒惰的跟随。
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource1) {
return builder
.dataSource(dataSource1)
.packages("com.example.demo.one.dto")
.build();
}
解决方案
简而言之
解决方案几乎很简单:做所有事情 Spring Boot 会做的。 仅 "Almost",因为这些机制中的大多数都依赖于自动配置(覆盖这些是一种代码味道,所以这不是实现它的方法)and/or internal/protected classes(这你不能直接打电话)。
可能会变脆?
这意味着您基本上必须将 Spring Boot 的代码复制到您自己的代码中,可能会对 Spring Boot 的未来升级造成一些脆弱性(或者只是您的代码不会受益于最新的 bug/performances 修复)。 从这方面来说,我不太喜欢我在这里提出的解决方案。
详细指南
你依赖的豆子
您需要将以下 bean 注入到您的数据源配置中:
org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties
org.springframework.boot.autoconfigure.orm.jpa.JpaProperties
List<org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer>
要执行的操作
借鉴 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration
,我添加了 hibernate.resource.beans.container
属性 定制器。
但是,我跳过了在我们的项目中不是问题的命名策略。
这为我提供了以下构造函数和方法:
public DataSource1Config(
JpaProperties jpaProperties,
HibernateProperties hibernateProperties,
ConfigurableListableBeanFactory beanFactory,
ObjectProvider<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers
) {
this.jpaProperties = jpaProperties;
this.hibernateProperties = hibernateProperties;
this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers(
beanFactory,
hibernatePropertiesCustomizers.orderedStream().collect(Collectors.toList())
);
}
private List<HibernatePropertiesCustomizer> determineHibernatePropertiesCustomizers(
ConfigurableListableBeanFactory beanFactory,
List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers
) {
List<HibernatePropertiesCustomizer> customizers = new ArrayList<>();
if (ClassUtils.isPresent("org.hibernate.resource.beans.container.spi.BeanContainer",
getClass().getClassLoader())) {
customizers.add((properties) -> properties.put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory)));
}
customizers.addAll(hibernatePropertiesCustomizers);
return customizers;
}
然后,根据 org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration
,我加载了供应商属性。
在这里,我再次跳过了一些您可以查看的自动自定义(JpaBaseConfiguration#customizeVendorProperties(Map)
及其在 subclasses 中的实现)。
private Map<String, Object> getVendorProperties() {
return new LinkedHashMap<>(
this.hibernateProperties
.determineHibernateProperties(jpaProperties.getProperties(),
new HibernateSettings()
// Spring Boot's HibernateDefaultDdlAutoProvider is not available here
.hibernatePropertiesCustomizers(this.hibernatePropertiesCustomizers)
)
);
}
完成配置class
作为参考,我在应用上述详细更改后向您提供我的完整配置 class。
package com.example.demo.config;
import org.hibernate.cfg.AvailableSettings;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.hibernate5.SpringBeanContainer;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.ClassUtils;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.example.demo.one.repository",
entityManagerFactoryRef = "dataSource1EntityManagerFactory",
transactionManagerRef = "TransactionManager1"
)
public class DataSource1Config {
private final JpaProperties jpaProperties;
private final HibernateProperties hibernateProperties;
private final List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers;
public DataSource1Config(
JpaProperties jpaProperties,
HibernateProperties hibernateProperties,
ConfigurableListableBeanFactory beanFactory,
ObjectProvider<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers
) {
this.jpaProperties = jpaProperties;
this.hibernateProperties = hibernateProperties;
this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers(
beanFactory,
hibernatePropertiesCustomizers.orderedStream().collect(Collectors.toList())
);
}
private List<HibernatePropertiesCustomizer> determineHibernatePropertiesCustomizers(
ConfigurableListableBeanFactory beanFactory,
List<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers
) {
List<HibernatePropertiesCustomizer> customizers = new ArrayList<>();
if (ClassUtils.isPresent("org.hibernate.resource.beans.container.spi.BeanContainer",
getClass().getClassLoader())) {
customizers.add((properties) -> properties.put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory)));
}
customizers.addAll(hibernatePropertiesCustomizers);
return customizers;
}
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.lib")
public DataSourceProperties dataSource1Properties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource dataSource1(DataSourceProperties dataSource1Properties) {
return dataSource1Properties.initializeDataSourceBuilder().build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(EntityManagerFactoryBuilder factoryBuilder, DataSource dataSource1) {
final Map<String, Object> vendorProperties = getVendorProperties();
return factoryBuilder
.dataSource(dataSource1)
.packages("com.example.demo.one.dto")
.properties(vendorProperties)
.build();
}
@Bean
@Primary
public PlatformTransactionManager transactionManager1(EntityManagerFactory dataSource1EntityManagerFactory) {
return new JpaTransactionManager(dataSource1EntityManagerFactory);
}
private Map<String, Object> getVendorProperties() {
return new LinkedHashMap<>(
this.hibernateProperties
.determineHibernateProperties(jpaProperties.getProperties(),
new HibernateSettings()
// Spring Boot's HibernateDefaultDdlAutoProvider is not available here
.hibernatePropertiesCustomizers(this.hibernatePropertiesCustomizers)
)
);
}
}
您可以简单地将 cacheManager
注入并放入您的 dataSource1EntityManagerFactory
中。无需使用 HibernatePropertiesCustomizer
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(
EntityManagerFactoryBuilder builder,
DataSource dataSource1,
javax.cache.CacheManager cacheManager) {
return builder
.dataSource(dataSource1)
.properties(Map.of(org.hibernate.cache.jcache.ConfigSettings.CACHE_MANAGER, cacheManager))
.packages("com.example.demo.one.dto")
.build();
}