即使使用@Fetch(FetchMode.JOIN),JPA + Hibernate 也有太多查询问题

kou*_*mln 4 java hibernate spring-data-jpa spring-boot

我正在使用 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<>();
}
Run Code Online (Sandbox Code Playgroud)

密码实体

@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;
    .......
}
Run Code Online (Sandbox Code Playgroud)

角色实体

@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;
    ....
}
Run Code Online (Sandbox Code Playgroud)

人物资料库

public interface PersonRepository extends CrudRepository<Person, Long> {

    Optional<Person> findByEmail(String email);

}
Run Code Online (Sandbox Code Playgroud)

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

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

附注。该@Data@NoArgsConstructor是龙目岛的注解。

Ken*_*han 5

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

public interface PersonRepository extends CrudRepository<Person, Long> {

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

}
Run Code Online (Sandbox Code Playgroud)

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