为什么在Hibernate/JPA中FetchType.LAZY不起作用?

9

I ve got the following object (simplified):

@Entity
@Table(name = "delivery_addresses")
data class DeliveryAddress (
        val street: String
) {

    @ManyToOne(fetch=FetchType.LAZY)
    lateinit var user: User
}

当我通过ID查询对象时

val deliveryAddress = deliveryAddressService.findById(1)

它是做什么的?

override fun findById(deliveryAddressId: Long): DeliveryAddress? {
    return deliveryAddressRepository.findById(deliveryAddressId).orElse(null) // JpaRepository
}

我可以看到执行了以下查询:

select deliveryad0_.street as street6_2_0_, deliveryad0_.user_id as user_id8_2_0_, from delivery_addresses deliveryad0_ where deliveryad0_.id=?

select user0_.id as id1_5_0_, user0_.email as email2_5_0_, user0_.password as password3_5_0_, where user0_.id=?

如何使FetchType.LAZY按预期工作(同时@Basic(fetch = FetchType.LAZY)对我也不起作用)?


通常情况下,如果该字段不是延迟加载的,则会进行连接操作。在日志中,您有一个单独的选择。因此,您可能在某个地方访问了该字段,这就是为什么它被加载的原因。例如,它可以在toString()中使用,或者如果您在调试时停止并查看变量,则IDE会访问字段以向您显示值,这也可能会加载延迟关联。或者可能Kotlin做了一些您不希望的事情(我不知道Kotlin,因此无法对具体情况发表评论)。对于此操作,您肯定不需要字节码增强。 - Stanislav Bashkyrtsev
这两个查询都在JdkDynamicAopProxy.java中执行,所以看起来不是这样。 - mleister
它们都被称为:MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); retVal = invocation.proceed(); - mleister
好的,这并没有告诉我们太多信息。这是一个通用的东西,是method在工作。顺便说一下,您可以将属性hibernate.use_sql_comments=true设置为EntitManagerFactory,也许它会告诉您为什么正在加载关联。另外一件事 - 您可以在getUser()方法或User的每个方法中设置断点,以找出哪些代码正在访问它。 - Stanislav Bashkyrtsev
DeliveryAddress类中的toString/equals/hashCode方法是否依赖于user字段?这可能是触发查询的“隐藏用法”。 - sp00m
谢谢大家,有了你们的帮助我成功解决了问题。 - mleister
1个回答

8

最终我终于弄清楚了。问题与kotlin数据类有关(参见KT-28525

不应该在spring-data-jpa中使用数据类。引自kotlin spring-guide

我们这里不使用带有val属性的数据类,因为JPA并不支持不可变类或数据类自动生成的方法。如果您使用其他Spring Data产品,大多数都支持这样的结构,因此在使用Spring Data MongoDB、Spring Data JDBC等时,应该使用数据类如data class User(val login: String,...)。

为了使惰性获取按预期工作,实体应该是open的。我的解决方案:

build.gradle.kts:

plugins {
  kotlin("plugin.allopen") version "1.3.61"
}

allOpen {
  annotation("javax.persistence.Entity")
  annotation("javax.persistence.Embeddable")
  annotation("javax.persistence.MappedSuperclass")
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接