在我们正在开发的这个应用程序中,我们注意到一个视图特别慢。我对视图进行了分析,并注意到hibernate执行的一个查询花费了10秒,即使数据库中只有两个对象需要获取。所有“一对多”和“多对多”关系都是懒惰的,所以这不是问题所在。在检查实际执行的SQL时,我注意到查询中有超过80个连接。

进一步检查这个问题,我注意到这个问题是由实体类之间一对一和多对一关系的深层层次结构引起的。所以,我想,我只要把它们设为lazy,就能解决问题了。但是注释@OneToOne(fetch=FetchType.LAZY)或@ManyToOne(fetch=FetchType.LAZY)似乎都不起作用。要么我得到一个异常,要么它们实际上没有被代理对象替换,从而变得懒惰。

你知道我要怎么做吗?注意,我没有使用persistence.xml来定义关系或配置细节,一切都是在java代码中完成的。


当前回答

这个问题已经很老了,但是在Hibernate 5.1.10中,有一些新的更好的解决方案。

惰性加载除了@OneToOne关联的父端外都有效。这是因为Hibernate没有其他方法来知道是给这个变量分配一个空值还是一个代理。更多细节可以在本文中找到

您可以激活延迟加载字节码增强 或者,您可以删除父端,并使用上面文章中解释的@MapsId的客户端。通过这种方式,你会发现你并不真正需要父端,因为子端与父端共享相同的id,所以你可以通过知道父端id轻松获取子端 .

其他回答

一对一关联的最有效映射 您可以通过为两个关联实体使用相同的主键值来避免所有这些问题并消除外键列。可以通过使用@MapsId注释关联的所属方来实现这一点。

@Entity
public class Book {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @OneToOne(mappedBy = "book", fetch = FetchType.LAZY, optional = false)
    private Manuscript manuscript;
 
    ...
}


@Entity
public class Manuscript {
 
    @Id
    private Long id;
 
    @OneToOne
    @MapsId
    @JoinColumn(name = "id")
    private Book book;
 
    ...
}


Book b = em.find(Book.class, 100L);
Manuscript m = em.find(Manuscript.class, b.getId());

更多详情请点击此url

如果关系必须不是双向的,那么@ElementCollection可能比使用惰性的One2Many集合更容易。

首先,对KLE的答案进行一些澄清:

Unconstrained (nullable) one-to-one association is the only one that can not be proxied without bytecode instrumentation. The reason for this is that owner entity MUST know whether association property should contain a proxy object or NULL and it can't determine that by looking at its base table's columns due to one-to-one normally being mapped via shared PK, so it has to be eagerly fetched anyway making proxy pointless. Here's a more detailed explanation. many-to-one associations (and one-to-many, obviously) do not suffer from this issue. Owner entity can easily check its own FK (and in case of one-to-many, empty collection proxy is created initially and populated on demand), so the association can be lazy. Replacing one-to-one with one-to-many is pretty much never a good idea. You can replace it with unique many-to-one but there are other (possibly better) options.

Rob H.有一个有效的观点,但是您可能无法根据您的模型实现它(例如,如果您的一对一关联是可空的)。

现在,就最初的问题而言

A) @ManyToOne(fetch=FetchType.LAZY)应该可以正常工作。您确定它没有在查询本身中被覆盖吗?可以在HQL中指定连接获取和/或通过Criteria API显式设置获取模式,这将优先于类注释。如果不是这样,你仍然有问题,请发布你的类,查询和结果的SQL进行更多的重点对话。

B) @一对一比较棘手。如果它绝对不是空的,那就用Rob H吧。的建议,并具体说明如下:

@OneToOne(optional = false, fetch = FetchType.LAZY)

否则,如果你可以改变你的数据库(添加一个外键列到所有者表),这样做,并将其映射为“joined”:

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name="other_entity_fk")
public OtherEntity getOther()

在OtherEntity中:

@OneToOne(mappedBy = "other")
public OwnerEntity getOwner()

如果您不能这样做(并且不能接受快速抓取),字节码插装就是惟一的选择。然而,我不得不同意CPerkins的观点——如果你有80个!!由于渴望一对一的联系,你有更大的问题:-)

Hibernate中xtoone的基本思想是它们在大多数情况下都不是懒惰的。

一个原因是,当Hibernate必须决定放置一个代理(带id)或null时, 无论如何,它都必须查看另一个表才能加入。访问数据库中另一个表的代价是巨大的,所以它最好在那个时刻为那个表获取数据(非懒惰行为),而不是在以后的请求中获取数据,这将需要对同一个表进行第二次访问。

编辑:具体请参考ChssPly76的回答。这一个不那么准确和详细,它没有提供任何东西。谢谢ChssPly76。

对于Kotlin开发者:为了允许Hibernate继承你想要惰性加载的@Entity类型,它们必须是可继承/开放的,而在Kotlin中它们默认不是这样的。为了解决这个问题,我们可以使用完全开放的编译器插件,并通过添加到我们的build.gradle来指导它处理JPA注释:

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

如果你像我一样使用Kotlin和Spring,你很可能也在使用Kotlin -jpa/no-args和Kotlin - Spring /all-open编译器插件。但是,您仍然需要添加上面的代码行,因为插件的组合不会打开这样的类。

阅读Léo Millon的文章,了解更多的解释。