使用 JPA 注释强制加入查询

Forcing join in the query using JPA annotations

我正在努力强制 Hibernate 在获取数据时在两个 table 之间使用连接。我们有一个大父 table 和一个大子 table,由多个列连接(没有单一 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 table 以获取所有记录,然后单独查询每条记录的评论。与其对每本书的每组评论分别查询,不如先做一个 left outer join 来获取它们似乎更有效率。

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

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

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

If you forget to JOIN FETCH all EAGER associations, Hibernate is going to issue a secondary select for each and every one of those which, in turn, can lead to N+1 query issues.

For this reason, you should prefer LAZY associations.

因此,我建议您更正此关联:

@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中所述:

The reason why we are not using a JPQL query to fetch multiple entities is because the FetchMode.JOIN strategy would be overridden by the query fetching directive.

...

Therefore, FetchMode.JOIN is useful for when entities are fetched directly, via their identifier or natural-id.

Also, the FetchMode.JOIN acts as a FetchType.EAGER strategy. Even if we mark the association as FetchType.LAZY, the FetchMode.JOIN will load the association eagerly.

因此,FetchMode.JOIN 仅适用于如下所示的直接获取:

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