gpt4 book ai didi

hibernate - 使用 Spring Boot 2.1+ 为 Hibernate 配置缓存

转载 作者:行者123 更新时间:2023-12-04 04:31:26 29 4
gpt4 key购买 nike

背景和问题

我正在尝试在 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: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);
    }
    }

    应用程序.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 将使用它的 AutoConfigurations 来创建一切都很好,包括供应商属性(您在 spring.jpa.properties 下的所有内容)、 hibernate 属性(在 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 会做的事情。
    仅“几乎”,因为大多数这些机制都依赖于 AutoConfigurations(覆盖这些是代码异味,所以这不是这样做的方法)和/或内部/ protected 类(您不能直接调用)。

    可能的脆性?

    这意味着您基本上必须将 Spring Boot 的代码复制到自己的代码中,这可能会对 Spring Boot 的 future 升级产生一些脆弱性 (或者只是您的代码不会从最新的错误/性能修复中受益)。
    从这方面来看,我不是我在这里提出的解决方案的忠实拥护者。

    详细指南

    你依赖的 bean

    您需要将以下 bean 注入(inject)到您的数据源配置中:
  • 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) 及其在子类中的实现)。

        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)
    )
    );
    }

    完整的配置类

    作为引用,一旦我应用了上面详述的更改,我就会为您提供完整的配置类。

    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)
    )
    );
    }
    }

    关于hibernate - 使用 Spring Boot 2.1+ 为 Hibernate 配置缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59679175/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com