当 Hibernate 查询缓存处于活动状态时,Wildfly 无法启动

Wildfly failed to start when Hibernate query cache is active

我们正在将我们的应用程序从 JBoss EAP 6.4 迁移到 WildFly 14。我们在使用 Hibernate 和 Infinispan 时遇到问题。

应用程序配置为使用 Hibernate 的二级缓存(也称为 2LC)和 Wildfly 提供的 Infinispan。

在启用 2LC 缓存但禁用查询缓存的情况下,应用程序启动并似乎正常工作。

但是当我们尝试再次启用查询缓存时(属性 hibernate.cache.use_query_cache 设置为 true) ,它在初始化期间崩溃,给出了连接的堆栈跟踪。

java.lang.ClassCastException: org.infinispan.hibernate.cache.v53.impl.DomainDataRegionImpl cannot be cast to org.hibernate.cache.spi.QueryResultsRegion

在调试时,我无法理解为什么管理二级缓存的Hibernate内部代码试图将实体缓存区域转换为查询结果缓存区域。

我试图通过使用缓存 local-query 定义 属性 hibernate.cache.infinispan.query.cfg 来为每个缓存使用单独的缓存 在 Infinispan 配置中定义,但 Hibernate 似乎没有考虑它。

我是缓存的新手,我必须承认,即使阅读了很多文档,我也没有完全理解我在做什么。

版本:

这里是我们关注的pom.xml中的Maven依赖声明(应用分为几个Maven项目)

所有这些都在 9.3 版本中。1.Final

这是存储在standalone.xml中的缓存容器配置:

        <subsystem xmlns="urn:jboss:domain:infinispan:7.0">
            ...
            <cache-container name="hibernate" module="org.infinispan.hibernate-cache">
                <local-cache name="entity">
                    <transaction mode="NON_XA"/>
                    <object-memory size="10000"/>
                    <expiration max-idle="100000"/>
                </local-cache>
                <local-cache name="local-query">
                    <object-memory size="10000"/>
                    <expiration max-idle="100000"/>
                </local-cache>
                <local-cache name="timestamps"/>
            </cache-container>
        </subsystem>

我们的 jboss-deployment-structure.xml.

的依赖项部分
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        ...
        <dependencies>
            <!-- Infinispan -->
            <module name="org.infinispan" /> 
            <module name="org.infinispan.commons" />
            <module name="org.infinispan.hibernate-cache"/>

        </dependencies>
    </deployment>
</jboss-deployment-structure>

我们的 Infinispan 配置文件:

<infinispan
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:infinispan:config:7.0 http://www.infinispan.org/schemas/infinispan-config-7.0.xsd"
    xmlns="urn:infinispan:config:7.0">
    <threads />

    <cache-container name="DefaultCacheManager"
        statistics="true">
        <transport />
        <jmx duplicate-domains="true" />

        <local-cache name="___defaultcache">
            <transaction mode="NONE" />
        </local-cache>

        <local-cache name="defaultCache">
            <transaction mode="NONE" />
            <expiration lifespan="1000" max-idle="1000" interval="500" />
            <memory>
                <object size="1000" />
            </memory>
            <persistence passivation="false">
                <file-store purge="false" read-only="false"
                    path="${jboss.server.temp.dir}/cacheservice" />
            </persistence>
        </local-cache>

        <local-cache name="local-query">
            <locking isolation="READ_COMMITTED" concurrency-level="1000"
                acquire-timeout="15000" striping="false" />
            <eviction max-entries="140000" strategy="LRU" />
            <expiration max-idle="1200000" />
            <transaction mode="NONE" auto-commit="false" />
        </local-cache>

        <!-- Other application caches ... -->

    </cache-container>
</infinispan>

WildFly 独立实例没有正确启动,而是在其初始化期间崩溃,当尝试装载 2LC 缓存时,并提供以下堆栈跟踪。

hibernate.cache.use_query_cache 设置为 false 可以解决问题,但我们确实需要查询缓存。

Caused by: java.lang.ClassCastException: org.infinispan.hibernate.cache.v53.impl.DomainDataRegionImpl cannot be cast to org.hibernate.cache.spi.QueryResultsRegion
    at org.hibernate.cache.internal.EnabledCaching.makeQueryResultsRegionAccess(EnabledCaching.java:491)
    at org.hibernate.cache.internal.EnabledCaching.getQueryResultsCache(EnabledCaching.java:478)
    at org.hibernate.loader.Loader.listUsingQueryCache(Loader.java:2515)
    at org.hibernate.loader.Loader.list(Loader.java:2498)
    at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:109)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1959)
    at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:370)
    at fr.bdf.interop.middle.dao.hibernate.HibernateGenericDao.find(HibernateGenericDao.java:110)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy204.find(Unknown Source)
    ...

谢谢,

我终于找到了解决方法。正如 Radim Vansa 所说,问题来自 entities/collection 缓存区域和查询缓存区域之间的冲突。

在与缓存相关的 Hibernate 文档中,我发现要为查询或条件定义缓存区域名称,必须使用 setCacheRegion(String) 方法。

通过在我们的代码中搜索所有这些方法调用,我终于意识到在我们的抽象通用 DAO class 中,我们定义查询区域名称如下:

public HibernateGenericDao(Class<E> type) {
    ...
    this.cacheRegion = type.getCanonicalName();
}

然后在此处使用 cacheRegion 字段:

criteria.setCacheRegion(getCacheRegion());

鉴于 entities/collections 缓存区域名称也由实体 class fully-qualified 名称定义(我们不知道如何),附加一个后缀查询缓存区域名称解决冲突,使应用程序正常启动。

this.cacheRegion = type.getCanonicalName().concat(QUERY_CACHE_REGION_PREFIX);

谢谢,

我迁移到WildFly 17.0后遇到了同样的问题。0.Final。应用程序服务器的模块文件夹中已经包含 Hibernate 5.3.10。在调查问题后,我发现实际上 Hibernate 版本有 https://hibernate.atlassian.net/browse/HHH-13586 bug. As I didn't have any plans to migrate our project from WildFly 17 to a newer version I decided to patch the current version of the Hibernate library. So I just downloaded 5.3.10 version and patched hibernate-core module by the https://github.com/hibernate/hibernate-orm/commit/2076c68ddff5dc39055e90e162a34c99c72261cb 提交,构建它并更改 WildFly 模块文件夹中的原始 hibernate-core lib。 java.lang.ClassCastException 消失了。也许它会对其他人有所帮助。