簡體   English   中英

使用@Fetch(FetchMode.JOIN)時,JPA + Hibernate的查詢問題太多

[英]Too many queries problem with JPA + Hibernate even when using @Fetch(FetchMode.JOIN)

我正在使用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() ,會為人員表中的每一行觸發選擇查詢,以便在我訪問此人時獲取密碼和角色。 我知道我可以在存儲庫中使用帶有JOIN FETCH @Query注釋來強制生成單個查詢,但我想知道是否還有其他方法可以這樣做。 我正在尋找一些我們可以在實體級別做的事情來減少查詢。

使用spring boot 2.1.5-RELEASE版本和相關的依賴項。

PS。 @Data@NoArgsConstructor是Lombok注釋。

我將按原樣保留實體,並使用@Query注釋覆蓋存儲庫中的findAll方法。 這樣,代碼重構是最小的(只有一個存儲庫更改而不是實體更改)。

最小的代碼更改是使用spring數據中的ad-hoc EntityGraph功能。 只需覆蓋PersonRepositoryfindAll()並使用@EntityGraph配置圖形。 此圖中的所有實體將一起提取。

public interface PersonRepository extends CrudRepository<Person, Long> {

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

}

在場景后面,它像JOIN FETCH一樣工作。 將僅生成具有LEFT JOIN的單個SQL。

您應該將@BatchSize放在Password類之上

@Entity
@Table(name = "cd_password")
@Data
@NoArgsConstructor
@BatchSize(size = 50)
public class Password {
...
}

以下是@BatchSize的查詢:

Hibernate: 
    select
        person0_.id as id1_1_,
        person0_.password_id as password2_1_ 
    from
        cd_person person0_
Hibernate: 
    select
        password0_.id as id1_0_0_,
        password0_.password_hash as password2_0_0_ 
    from
        cd_password password0_ 
    where
        password0_.id in (
            ?, ?, ?, ?, ?
        )

對您的問題的不滿意的答案是:不,沒有辦法注釋/配置實體,以便獲取模式也適用於查詢。

正如您自己發現的那樣,您可以操縱查詢本身 替代方案是使用Hibernate的獲取配置文件或利用JPA實體圖 - 但所有這些都需要在查詢/會話級別進行編程干預。

你不能使用懶惰的提取並刪除@Fetch嗎? 在您的實體上使用@NamedQuery並使用hibernate會話在自定義服務中調用session.createNamedQuery就可以了。

如果您能夠不使用默認的personRepository.findAll()但是這個自定義服務可以運行優化查詢。 我知道它並沒有完全回答你的問題,但是我的團隊和我面臨着完全相同的問題,這就是我們如何做到的。

我的建議是:

  1. 嘗試重構並使用延遲抓取。
  2. 我可能不太了解這一部分,但為什么你需要personRepository.findAll()呢? 我認為你只需要像personRepository.findById()這樣的東西,這樣你就可以輕松獲取角色和其他數據。 選擇所有人似乎是一個巨大的超負荷。
  3. 您可能稍后需要JpaRepository的擴展函數,因此現在可能值得更改它而不是稍后再工作。

這應該有效:

public interface PersonRepository extends CrudRepository<Person, Long> {
     @Override
        @Query("SELECT p FROM Person p JOIN FETCH p.roles JOIN FETCH p.password ")
        Iterable<Person> findAll();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM