gpt4 book ai didi

hibernate - 当只需要实体 id 时如何避免初始化 Hibernate 代理

转载 作者:行者123 更新时间:2023-12-02 00:57:11 25 4
gpt4 key购买 nike

对于 @ManyToOne JPA 实体中的关系我只对实际的 id 引用感兴趣,而不是获取与关系关联的整个模型。

以这些 Kotlin JPA 实体为例:

@Entity
class Continent(
@Id
var id: String,
var code: String,
var name: String
) : Comparable<Continent> {

companion object {
private val COMPARATOR = compareBy<Continent> { it.id }
}

override fun compareTo(other: Continent): Int {
return COMPARATOR.compare(this, other)
}
}

@Entity
class Country(
@Id
var id: String,
var alpha2Code: String,
var alpha3Code: String,
var name: String,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "continent_id")
var continent: Continent

) : Comparable<Country> {

companion object {
private val COMPARATOR = compareBy<Country> { it.id }
}

override fun compareTo(other: Country): Int {
return COMPARATOR.compare(this, other)
}
}

现在当我访问 country.continent.id从我的 Kotlin 代码中获取完整的 Continent实际上是从数据库中查询的。
这太过分了,因为我只对 Continent.id 感兴趣.

我试过添加 @Access(AccessType.PROPERTY)喜欢:
@Entity
class Continent(
@Id
@Access(AccessType.PROPERTY)
var id: String,

但这没什么区别。整机 Continent仍然从数据库中查询。

我试过 @Access(AccessType.PROPERTY)正如在其他帖子中提到的(如 Hibernate one-to-one: getId() without fetching entire object),但我已经注意到对此的不同反馈。

我正在使用 Hibernate 5.3.7.Final与 Kotlin 1.3.0 .

我想知道是否 1) @Access(AccessType.PROPERTY)方法是正确的,2)这也应该与 Kotlin 一起使用吗?也许 Kotlin 生成 Java 代码的方式导致了问题?

更新

我创建了一个简单的测试项目,证明正在查询大陆。
https://github.com/marceloverdijk/hibernate-proxy-id

该项目包含一个简单的测试检索 country.continent.id并且启用了 SQL 日志记录。从日志中可以看出大陆是被查询的。

更新 2

我创建了 https://youtrack.jetbrains.net/issue/KT-28525为了这。

最佳答案

此行为由 JPA 规范定义,该规范要求在访问任何属性时获取关联,甚至是标识符。

传统上,Hibernate 在访问其标识符时不会初始化实体代理,但这种行为与 JPA 规范不一致,因此需要显式禁用此 JPA 合规性策略。

事实上,我在 Hibernate ORM 中创建了这两个测试用例,一切都按预期工作:

  • ManyToOneLazyLoadingByIdJpaComplianceTest
  • ManyToOneLazyLoadingByIdTest

  • 默认情况下,仅访问 id 时不会初始化 Proxy。

    这是测试:
    Continent continent = doInJPA( this::entityManagerFactory, entityManager -> {
    Country country = entityManager.find( Country.class, 1L );

    country.getContinent().getId();

    return country.getContinent();
    } );

    assertEquals( 1L, (long) continent.getId());

    assertProxyState( continent );

    默认情况下,这是预期的行为:
    protected void assertProxyState(Continent continent) {
    try {
    continent.getName();

    fail( "Should throw LazyInitializationException!" );
    }
    catch (LazyInitializationException expected) {

    }
    }

    但是,如果我们切换到 JPA 兼容性 moe:
    <property name="hibernate.jpa.compliance.proxy" value="false"/>

    这是我们得到的:
    protected void assertProxyState(Continent continent) {
    assertEquals( "Europe", continent.getName() );
    }

    因此,一切都按预期进行。

    问题来自 Kotlin 或 Spring Data JPA。您需要进一步调查它,看看为什么 Proxy 会被初始化。

    很可能是因为 toStringcompare实现添加到 Continent实体。

    关于hibernate - 当只需要实体 id 时如何避免初始化 Hibernate 代理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53466913/

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