【问题标题】:Forcing join in the query using JPA annotations使用 JPA 注释强制加入查询
【发布时间】:2021-08-15 05:04:50
【问题描述】:

我正在努力强制 Hibernate 在获取数据时使用两个表之间的连接。我们有一个大的父表和一个大的子表,由多个列连接(没有单一的 ID 列)。当我们获取父记录时,Hibernate 急切地获取子记录,但它使用单独的查询来完成,每个父记录一个。与只有一个包含连接的查询相比,这似乎非常慢。

但是如何让 Hibernate 进行连接呢?

以下是实体(这只是一个匿名示例):

@Entity
@Table(name = "BOOKS")
public class Book
{
    @EmbeddedId
    private BookID id;

    @Column(name = "DESCRIPTION")
    private String description;

    @Column(name = "PUBLISH_DATE")
    private Date publishDate;

    @OneToMany(mappedBy = "book", fetch = EAGER, cascade = ALL, orphanRemoval = true)
    private List<BookReview> reviews;
}

@Entity
@Table(name = "BOOK_REVIEWS")
public class BookReview
{
    @EmbeddedId
    private BookReviewID id;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "TITLE", referencedColumnName = "TITLE"),
        @JoinColumn(name = "AUTHOR_FIRST_NAME", referencedColumnName = "AUTHOR_FIRST_NAME"),
        @JoinColumn(name = "AUTHOR_LAST_NAME", referencedColumnName = "AUTHOR_LAST_NAME")
    })
    private Book book;

    @Column(name = "UPDATE_DATE")
    private Date updateDate;
}

...及其嵌入的键:

@Embeddable
public class BookID
{
    @Column(name = "TITLE")
    private String title;

    @Column(name = "AUTHOR_FIRST_NAME")
    private String authorFirstName;
    
    @Column(name = "AUTHOR_LAST_NAME")
    private String authorLastName;
}

@Embeddable
public class BookReviewID extends BookID
{
    @Column(name = "USERNAME")
    private String userName;
}

当我对 BOOK 中的所有记录(使用 entityManager.createQuery("from Book"))运行查询时,它会在 BOOK 表上运行一个查询以获取所有记录,然后单独查询每个记录的评论时间>。与其对每本书的每组评论进行单独查询,不如一开始就通过left outer join 来获取它们似乎会更有效。

这甚至可以通过注释实现吗?还是我必须使用CriteriaBuilder 或查询语言?

我应该注意,添加 @Fetch(FetchMode.JOIN) 并不会改变它在幕后所做的任何事情。

【问题讨论】:

标签: java hibernate jpa annotations


【解决方案1】:

Hibernate documentation 强烈建议使用fetch = FetchType.LAZY 而不是fetch = FetchType.EAGER

如果您忘记 JOIN FETCH 所有 EAGER 关联,Hibernate 将为每个关联发出辅助选择,这反过来可能导致 N+1 查询问题。

因此,您应该更喜欢 LAZY 关联。

所以,我建议你纠正这种关联:

@OneToMany(mappedBy = "book", fetch = EAGER, cascade = ALL, orphanRemoval = true)
private List<BookReview> reviews;

到这里:

@OneToMany(mappedBy = "book", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookReview> reviews;

然后在 HQL 中使用 join fetch 覆盖加入关联的惰性,如下所示:

entityManager.createQuery(
  "select b from Book b join fetch b.reviews"
)

至于@Fetch(FetchMode.JOIN),正如documentation中所述:

我们不使用 JPQL 查询来获取多个实体的原因是,FetchMode.JOIN 策略将被查询获取指令覆盖。

...

因此,FetchMode.JOIN 在通过实体标识符或自然 ID 直接获取实体时很有用。

此外,FetchMode.JOIN 充当FetchType.EAGER 策略。即使我们将关联标记为FetchType.LAZYFetchMode.JOIN 也会立即加载关联。

因此,FetchMode.JOIN 仅适用于如下直接提取:

Book book = entityManager.find(Book.class, new BookID(...));

【讨论】:

  • 这就是我害怕的。我们对调用EntityManager 的逻辑有大量抽象,以使其通用并对开发人员隐藏,但看起来我必须更改那部分。谢谢!
猜你喜欢
  • 2011-10-16
  • 2018-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-16
  • 1970-01-01
  • 2018-02-08
相关资源
最近更新 更多