背景和问题
我试图在Spring Boot 2.2中使用EHCache配置Hibernate,但似乎我做错了些什么。 我查看了几个教程和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:cache-api - org.ehcache:ehcache - org.projectlombok:lombok作为一种舒适方式
缓存配置
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