4

我有 2 个实体处于一对一的关系中(可以是多对一,但对于这种情况,我认为这并不重要),如下所示:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
 
    //...
}
@Entity
public class Post {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String subject;
 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private User otherUser;
     
    //...
}

The repository looks like this
@EntityGraph(attributePaths = {"otherUser"})
Iterable<Post> findAll(Predicate predicate);

所以我覆盖了findAll他们默认提供的方法,因为我仍然想使用谓词并且我仍然想将查询连接到另一个表。这几乎完美无缺。创建的查询如下所示

select
post0_.id as id1_4_0_,
user0_.id as id1_0_1_,
post0_.subject as subject2_4_0_,
user0_.name as name2_0_1_,
user0_.email as email3_0_1_
from
    Post post0_
    left outer join User user0_ on post0_.id = user0_.id
where
   ***contents of Predicate****

现在的问题是,如果用户从该查询中返回基本上为空(用户的所有属性为空,意味着不存在与条件匹配的属性),那么 Spring 会直接向用户表创建额外的选择查询以查找用户。例如,假设有一个 id=4 的帖子。如果没有 id = 4 的用户,那么 Spring 将创建并运行一个正常的查询,如下所示:

select
user0_.id as id1_0_1_,
user0_.name as name2_0_1_,
user0_.email as email3_0_1_
from
    User user0_ 
    left outer join User user0_ on post0_.id = user0_.id
where
   user0_id = 4

如果数据集很大并且包含不适合原始连接查询的大量记录,则会出现问题,那么将创建和执行太多额外的查询。如果它不符合连接条件,那么我希望忽略该实体记录。使用该示例,我希望忽略任何 id 与 post id 不匹配的用户,而不是另一个 select 语句。

有没有办法防止创建这些额外的选择查询?

4

1 回答 1

1

假设您通过日志语句使用 Hibernate,我相信您遇到了 n+1 sql 负载情况,需要添加不同/额外的 FETCH 指令以使otherUser关联快速获取,因此不需要后续的 select 语句.

我能找到的关于这个主题的最权威的文档在这里:https ://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#fetching

我建议您首先将您的更改FetchType.LAZYFetchType.EAGER. 如果将 JPA 注释FetchType.EAGER放在otherUser成员变量上并不能解决您的问题,请考虑利用@Fetch(FetchMode.JOIN)它来进一步加强它。

此外,为了完整起见,如果您选择使用基于查询的方法来检索(即 JPQL),那么您可以在您的 JPQL 中包含 FETCH 指令以根据需要实现效果。

于 2021-04-19T03:43:12.350 回答