【问题标题】:JHipster - Problem with Hibernate 2nd cache / ehcache trying to eject JHipster from a JHipster generated projectJHipster - Hibernate 2nd cache / ehcache 试图从 JHipster 生成的项目中弹出 JHipster 的问题
【发布时间】:2019-01-23 11:03:08
【问题描述】:

我正在尝试创建一个基于 JHipster 生成的通用 Spring Boot 后端模板(因为某些应用程序不会由我维护,并且其他 Spring 开发人员可能必须适应 jhipster 才能对 jhipster 进行更改方法)。也就是说,我遵循的过程是:使用以下设置生成两个 jhipster 项目:

{
    "generator-jhipster": {
        "promptValues": {
            "packageName": "com.mypackage.springtemplate",
            "nativeLanguage": "en"
        },
        "jhipsterVersion": "5.7.2",
        "applicationType": "monolith",
        "baseName": "springtemplate",
        "packageName": "com.mypackage.springtemplate",
        "packageFolder": "com/mypackage/springtemplate",
        "serverPort": "8080",
        "authenticationType": "jwt",
        "cacheProvider": "ehcache",
        "enableHibernateCache": true,
        "websocket": "spring-websocket",
        "databaseType": "sql",
        "devDatabaseType": "mysql",
        "prodDatabaseType": "mysql",
        "searchEngine": false,
        "messageBroker": false,
        "serviceDiscoveryType": false,
        "buildTool": "maven",
        "enableSwaggerCodegen": false,
        "jwtSecretKey": "****",
        "clientFramework": "angularX",
        "useSass": true,
        "clientPackageManager": "npm",
        "testFrameworks": [],
        "jhiPrefix": "jhi",
        "otherModules": [],
        "enableTranslation": true,
        "nativeLanguage": "en",
        "languages": ["en", "es"]
    }
}

其中之一是作为参考。第二个是把 jhipster 东西拿出来的项目。我遵循的步骤是:

  1. 删除了 jhipster BOM 并包含了现在缺少的依赖项。
  2. 在本地复制使用的 JHipster 框架类(主要用于配置和实用程序)。
  3. 将提及的“JHipster”重命名为“应用程序”。
  4. 将 application-*.yml hispter 框架引用更改为本地引用。

这些是项目的主要结构变化:

问题是当我尝试运行该项目时,我在 Hibernate 2nd level cache / ehcache 中遇到以下错误:

Caused by: java.lang.IllegalStateException: All Hibernate caches should be created upfront. Please update CacheConfiguration.java to add com.mypackage.springtemplate.domain.User
    at com.mypackage.springtemplate.config.cache.NoDefaultJCacheRegionFactory.createCache(NoDefaultJCacheRegionFactory.java:24)
    at org.hibernate.cache.jcache.JCacheRegionFactory.getOrCreateCache(JCacheRegionFactory.java:190)
    at org.hibernate.cache.jcache.JCacheRegionFactory.buildEntityRegion(JCacheRegionFactory.java:113)
    at org.hibernate.cache.spi.RegionFactory.buildEntityRegion(RegionFactory.java:132)

application-dev.yml hibernate 配置指向新的本地类:

jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    database: MYSQL
    show-sql: true
    properties:
        hibernate.id.new_generator_mappings: true
        hibernate.connection.provider_disables_autocommit: true
        hibernate.cache.use_second_level_cache: true
        hibernate.cache.use_query_cache: false
        hibernate.generate_statistics: true
        hibernate.cache.region.factory_class: com.mypackage.springtemplate.config.cache.BeanClassLoaderAwareJCacheRegionFactory

缓存关联的类保持不变...

BeanClassLoaderAwareJCacheRegionFactory.java

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.Properties;

import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.cache.spi.CachingProvider;

/**
 * Fixes Spring classloader issues that were introduced in Spring Boot 2.0.3.
 *
 * This allows to use the same classloader for ehcache, both for the Spring Cache abstraction and for the Hibernate
 * 2nd level cache.
 *
 * See https://github.com/jhipster/generator-jhipster/issues/7783 for more information.
 */
public class BeanClassLoaderAwareJCacheRegionFactory extends NoDefaultJCacheRegionFactory {

    private static volatile ClassLoader classLoader;

    @Override
    protected CacheManager getCacheManager(Properties properties) {
        Objects.requireNonNull(classLoader, "Please set Spring's classloader in the setBeanClassLoader " +
            "method before using this class in Hibernate");
        CachingProvider cachingProvider = getCachingProvider( properties );
        String cacheManagerUri = getProp( properties, CONFIG_URI );

        URI uri = getUri(cachingProvider, cacheManagerUri);
        CacheManager cacheManager = cachingProvider.getCacheManager(uri, classLoader);

        // To prevent some class loader memory leak this might cause
        setBeanClassLoader(null);

        return cacheManager;
    }

    private URI getUri(CachingProvider cachingProvider, String cacheManagerUri) {
        URI uri;
        if (cacheManagerUri != null) {
            try {
                uri = new URI(cacheManagerUri);
            }
            catch (URISyntaxException e) {
                throw new CacheException("Couldn't create URI from " + cacheManagerUri, e);
            }
        }
        else {
            uri = cachingProvider.getDefaultURI();
        }
        return uri;
    }

    /**
     * This method must be called from a Spring Bean to get the classloader.
     * For example: BeanClassLoaderAwareJCacheRegionFactory.setBeanClassLoader(this.getClass().getClassLoader());
     *
     * @param classLoader The Spring classloader
     */
    public static void setBeanClassLoader(ClassLoader classLoader) {
        BeanClassLoaderAwareJCacheRegionFactory.classLoader = classLoader;
    }
} 

NoDefaultJCacheRegionFactory.java

import java.util.Properties;
import javax.cache.Cache;

import org.hibernate.cache.jcache.JCacheRegionFactory;
import org.hibernate.cache.spi.CacheDataDescription;

/**
 * Extends the default {@code JCacheRegionFactory} but makes sure all caches already exist to prevent
 * spontaneous creation of badly configured caches (e.g. {@code new MutableConfiguration()}.
 *
 * See http://www.ehcache.org/blog/2017/03/15/spontaneous-cache-creation.html for more information.
 */
@SuppressWarnings("serial")
public class NoDefaultJCacheRegionFactory extends JCacheRegionFactory {

    public static final String EXCEPTION_MESSAGE = "All Hibernate caches should be created upfront. " +
        "Please update CacheConfiguration.java to add";

    @Override
    protected Cache<Object, Object> createCache(String regionName, Properties properties, CacheDataDescription
        metadata) {
        throw new IllegalStateException(EXCEPTION_MESSAGE + " " + regionName);
    }
}

CacheConfiguration.java

import java.time.Duration;

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.cache.JCacheManagerCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.mypackage.springtemplate.config.properties.ApplicationProperties;

@Configuration
@EnableCaching
public class CacheConfiguration {

    private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration;

    public CacheConfiguration(ApplicationProperties applicationProperties) {
        BeanClassLoaderAwareJCacheRegionFactory.setBeanClassLoader(this.getClass().getClassLoader());
        ApplicationProperties.Cache.Ehcache ehcache =
            applicationProperties.getCache().getEhcache();

        jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(
            CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class,
                ResourcePoolsBuilder.heap(ehcache.getMaxEntries()))
                .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds())))
                .build());
    }

    @Bean
    public JCacheManagerCustomizer cacheManagerCustomizer() {
        return cm -> {
            cm.createCache(com.mypackage.springtemplate.repository.UserRepository.USERS_BY_LOGIN_CACHE, jcacheConfiguration);
            cm.createCache(com.mypackage.springtemplate.repository.UserRepository.USERS_BY_EMAIL_CACHE, jcacheConfiguration);
            cm.createCache(com.mypackage.springtemplate.domain.User.class.getName(), jcacheConfiguration);
            cm.createCache(com.mypackage.springtemplate.domain.Authority.class.getName(), jcacheConfiguration);
            cm.createCache(com.mypackage.springtemplate.domain.User.class.getName() + ".authorities", jcacheConfiguration);
        };
    }
}

已经检查过this question,但戴夫的目的与我的不同。事实上,我并没有对 jhipster 生成的缓存实现进行任何更改。

我有什么遗漏的吗? liquibase 可能会做我错过的事情吗?

【问题讨论】:

    标签: java spring hibernate jhipster ehcache


    【解决方案1】:

    也有可能根本没有调用CacheConfiguration.cacheManagerCustomizer。这意味着您已经破坏了缓存配置,并且可能没有使用缓存或简单缓存,而不是 Ehcache。请检查javax.cache 是否在您的类路径中正确。

    另一种可能性是您没有为 Hibernate 和 Spring 使用相同的 CacheManage。请调试到EhcacheCachingProvider 并检查仅创建了一个CacheManager。如果不是这种情况,你需要看看有什么不同。它通常是类加载器,但 BeanClassLoaderAwareJCacheRegionFactory 通常应该阻止它。

    【讨论】:

    • 已经确定它被调用了。我重新开始,目前正在工作。我删除了所有 io.github.jhipster 类(包括在项目包中),但在我的 pom.xml 中仍然有依赖管理中的 jhipster-dependencies 和 jhipster-framework。如果我试图摆脱它们(在我的 pom 中包含所需的依赖项),那么缓存将按描述停止工作。你可以看看github.com/sebastiandg7/springboot-backend-template
    • 它可以在我的机器上运行 :-)(说真的)但是我已经更新了我的解决方案以添加一个选项。
    猜你喜欢
    • 1970-01-01
    • 2019-06-19
    • 1970-01-01
    • 2014-10-29
    • 2021-04-15
    • 2014-02-14
    • 2017-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多