当启用Hibernate第二个缓存时,@ManyToOne的懒惰加载失败。

问题描述 投票:0回答:1

我在项目中创建了两个非常简单的实体。

@Entity
@Access(FIELD)
public class TestA implements Serializable
{
    @Id
    private UUID id;
    @Version
    private Long hVersion;

    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    private TestB testB;

    // ...
}


@Entity
@Access(FIELD)
public class TestB implements Serializable
{
    @Id
    private UUID id;
    @Version
    private Long hVersion;

    // ...
}

我们有一个可选的 @ManyToOne 从TestA到TestB的关系。

当我试图获取一个TestA实例时,像这样。

entityManager.find(TestA.class, myId);

我得到了两个select:一个是TestA,但也有一个是TestB,因为它正在急切地加载,这是不应该发生的。

Hibernate: select testa0_.id as id1_20_0_, testa0_.h_version as h_versio2_20_0_, testa0_.test_b_id as test_b_i3_20_0_ from test_a testa0_ where testa0_.id=?
Hibernate: select testb0_.id as id1_21_0_, testb0_.h_version as h_versio2_21_0_ from test_b testb0_ where testb0_.id=?

我尝试了所有这些组合,甚至为了测试而将关系设置为非选择。

@ManyToOne(fetch = FetchType.LAZY, optional = true)
private TestB testB;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
private TestB testB;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@LazyToOne(LazyToOneOption.PROXY)
private TestB testB;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@LazyToOne(LazyToOneOption.NO_PROXY)
private TestB testB;

这并没有改变什么,TestB还是急切地被加载。

然而,当我在关闭二级缓存的时候,在 persistence.xml,像这样。

<properties>
    <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
    <property name="hibernate.show_sql" value="true" />
    <property name="hibernate.debug" value="false" />
    <property name="hibernate.cache.use_second_level_cache" value="false" />
    <property name="hibernate.cache.use_query_cache" value="false" />
</properties>

现在TestB被懒加载了,我只在访问TestA.getTestB()的时候看到第二个选择查询。

当我在看日志的时候,我可以看到,在启用二级缓存的情况下,Hibernate解析testB将其放入缓存。

DEBUG [org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl] (default task-4) Hibernate RegisteredSynchronization successfully registered with JTA platform
DEBUG [org.hibernate.SQL] (default task-4) select testa0_.id as id1_20_0_, testa0_.h_version as h_versio2_20_0_, testa0_.test_b_id as test_b_i3_20_0_ from test_a testa0_ where testa0_.id=?
DEBUG [org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl] (default task-4) Starting ResultSet row #0
DEBUG [org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl] (default task-4) On call to EntityIdentifierReaderImpl#resolve, EntityKey was already known; should only happen on root returns with an optional identifier specified
DEBUG [org.hibernate.engine.internal.TwoPhaseLoad] (default task-4) Resolving associations for [com.monde3.lpt.veriqualis.model.test.TestA#00000000-9999-1111-1111-000000000001]
DEBUG [org.hibernate.engine.internal.TwoPhaseLoad] (default task-4) Adding entity to second-level cache: [com.monde3.lpt.veriqualis.model.test.TestA#00000000-9999-1111-1111-000000000001]
DEBUG [org.hibernate.internal.SessionImpl] (default task-4) Initializing proxy: [com.monde3.lpt.veriqualis.model.test.TestB#00000000-9999-1111-1111-000000000002]
DEBUG [org.hibernate.SQL] (default task-4) select testb0_.id as id1_21_0_, testb0_.h_version as h_versio2_21_0_ from test_b testb0_ where testb0_.id=?
DEBUG [org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl] (default task-4) Starting ResultSet row #0
DEBUG [org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl] (default task-4) On call to EntityIdentifierReaderImpl#resolve, EntityKey was already known; should only happen on root returns with an optional identifier specified
DEBUG [org.hibernate.engine.internal.TwoPhaseLoad] (default task-4) Resolving associations for [com.monde3.lpt.veriqualis.model.test.TestB#00000000-9999-1111-1111-000000000002]
DEBUG [org.hibernate.engine.internal.TwoPhaseLoad] (default task-4) Adding entity to second-level cache: [com.monde3.lpt.veriqualis.model.test.TestB#00000000-9999-1111-1111-000000000002]
DEBUG [org.hibernate.engine.internal.TwoPhaseLoad] (default task-4) Done materializing entity [com.monde3.lpt.veriqualis.model.test.TestB#00000000-9999-1111-1111-000000000002]
DEBUG [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl] (default task-4) Skipping aggressive release due to registered resources
DEBUG [org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader] (default task-4) Done entity load : com.monde3.lpt.veriqualis.model.test.TestB#00000000-9999-1111-1111-000000000002

我很无奈,因为我无法解释这种行为。我的代码中是否有错误或不好的做法?

java hibernate jpa lazy-loading many-to-one
1个回答
1
投票

我找到了!

为了与JPA完全兼容,当任何一个字段被访问时,代理应该被初始化。甚至是标识符 (在Hibernate中不是这样,以避免查询循环)。

由于在Hibernate中没有这种情况,所以避免了查询循环。Hibernate 5.2.13在Hibernate 5.2.13中,有一个新的选项来实现这个规则。hibernate.jpa.compliance.proxy,默认设置为 false 为了与Hibernate之前的行为保持一致。

但是!由于 Wildfly 14,该选项默认设置为 true 在服务器上下文中。我的代码是在WF18实例上运行的,所以是一样的。

解决方法是,像在 这另一个问题中的属性,以覆盖 persistence.xml,将其重置为默认值。false:

<property name="hibernate.jpa.compliance.proxy" value="false" />
© www.soinside.com 2019 - 2024. All rights reserved.