![](/img/trans.png)
[英]Why does Hibernate execute multiple SELECT queries instead of one when using @Fetch(FetchMode.JOIN)
[英]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功能。 只需覆蓋PersonRepository
的findAll()
並使用@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()
但是這個自定義服務可以運行優化查詢。 我知道它並沒有完全回答你的問題,但是我的團隊和我面臨着完全相同的問題,這就是我們如何做到的。
我的建議是:
personRepository.findAll()
呢? 我認為你只需要像personRepository.findById()
這樣的東西,這樣你就可以輕松獲取角色和其他數據。 選擇所有人似乎是一個巨大的超負荷。 這應該有效:
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.