Mar*_*ijk 5 proxy hibernate jpa kotlin
对于@ManyToOneJPA 实体中的关系,我只对实际 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)
}
}
Run Code Online (Sandbox Code Playgroud)
现在,当我从 Kotlin 代码访问时,实际上是从数据库查询country.continent.id完整内容。Continent这太过分了,因为我只对Continent.id.
我尝试添加@Access(AccessType.PROPERTY)如下:
@Entity
class Continent(
@Id
@Access(AccessType.PROPERTY)
var id: String,
Run Code Online (Sandbox Code Playgroud)
但这没有什么区别。整体Continent还是从数据库中查询。
我尝试了@Access(AccessType.PROPERTY)其他帖子中提到的方法(例如Hibernate 一对一: getId() 而不获取整个对象),但我已经注意到对此的混合反馈。
我将 Hibernate5.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
此行为由 JPA 规范定义,该规范要求在访问任何属性(甚至标识符)时获取关联。
传统上,Hibernate 在访问其标识符时不会初始化实体代理,但这种行为与 JPA 规范不一致,因此需要显式禁用此 JPA 合规性策略。
事实上,我在 Hibernate ORM 中创建了这两个测试用例,一切都按预期工作:
默认情况下,仅访问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 );
Run Code Online (Sandbox Code Playgroud)
默认情况下,这是预期的行为:
protected void assertProxyState(Continent continent) {
try {
continent.getName();
fail( "Should throw LazyInitializationException!" );
}
catch (LazyInitializationException expected) {
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我们切换到 JPA 兼容性 moe:
<property name="hibernate.jpa.compliance.proxy" value="false"/>
Run Code Online (Sandbox Code Playgroud)
这就是我们得到的:
protected void assertProxyState(Continent continent) {
assertEquals( "Europe", continent.getName() );
}
Run Code Online (Sandbox Code Playgroud)
因此,一切都按预期进行。
问题来自 Kotlin 或 Spring Data JPA。您需要进一步调查它并了解代理为何被初始化。
最有可能的是因为添加到实体中的toString或实现。compareContinent
| 归档时间: |
|
| 查看次数: |
4041 次 |
| 最近记录: |