首页 > 解决方案 > 即使使用 @Fetch(FetchMode.JOIN),JPA + Hibernate 也会出现太多查询问题

问题描述

我正在使用 Spring Boot 开发 REST 应用程序,并且正在尝试优化查询的性能。我目前正在使用findAll导致性能问题的存储库。代码如下:

个人实体

@Entity
@Table(name = "cd_person")
@Data
@NoArgsConstructor
public class Person {
    ....
    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "password_id")
    @Fetch(FetchMode.JOIN)
    private Password password;
    ....
    @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinTable(name = "cd_person_role",
        joinColumns = @JoinColumn(name = "person_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
    @Fetch(FetchMode.JOIN)
    private Set<Role> roles = new HashSet<>();
}

密码实体

@Entity
@Table(name = "cd_password")
@Data
@NoArgsConstructor
public class Password {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;

    @Column(name = "password_hash", nullable = false)
    private String passwordHash;
    .......
}

角色实体

@Entity
@Table(name = "cd_role")
@Data
@NoArgsConstructor
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "role_type")
    @Enumerated(EnumType.STRING)
    private RoleType roleType;
    ....
}

个人资料库

public interface PersonRepository extends CrudRepository<Person, Long> {

    Optional<Person> findByEmail(String email);

}

当我这样做时personRepository.findAll(),会为 person 表中的每一行触发选择查询,以在我访问该人时获取密码和角色。我知道我可以在存储库中使用@Query注释JOIN FETCH来使其强制生成单个查询,但我想知道是否还有其他方法可以这样做。我正在寻找我们可以在实体级别做的事情以减少查询。

使用spring boot 2.1.5-RELEASE版本及相关依赖。

PS。和是@DataLombok@NoArgsConstructor注释。

标签: javahibernatespring-bootspring-data-jpa

解决方案


最小的代码更改是使用 spring data 中的 ad-hoc EntityGraph 功能。只需覆盖PersonRepository'sfindAll()并用于@EntityGraph配置图形。此图中的所有实体将一起获取。

public interface PersonRepository extends CrudRepository<Person, Long> {

    @EntityGraph(attributePaths = { "password", "roles" })
    public List<Person> findAll();

}

在幕后它的工作原理就像JOIN FETCH. 只会生成带有 LEFT JOIN 的单个 SQL。


推荐阅读