[英]Spring data, JPA, Hibernate - bidirectional relationship infinite recursion
[英]Spring Data JPA - bidirectional relation with infinite recursion
首先,這是我的實體。
球員:
@Entity
@JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Player {
// other fields
@ManyToOne
@JoinColumn(name = "pla_fk_n_teamId")
private Team team;
// methods
}
團隊:
@Entity
@JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Team {
// other fields
@OneToMany(mappedBy = "team")
private List<Player> members;
// methods
}
正如許多主題已經說明的那樣,您可以通過 Jackson 以多種方式避免 WebService 中的 StackOverflowExeption。
這很酷,但 JPA 仍然在序列化之前構造一個具有無限遞歸到另一個實體的實體。 這很丑陋,因為請求需要更長的時間。 檢查此屏幕截圖: IntelliJ 調試器
有辦法解決嗎? 知道我想要不同的結果取決於端點。 例子:
謝謝!
編輯:也許這個問題不是很清楚,給出了我得到的答案,所以我會盡量更准確。
我知道可以通過 Jackson(@JSONIgnore、@JsonManagedReference/@JSONBackReference 等)或通過映射到 DTO 來防止無限遞歸。 我仍然看到的問題是:以上都是查詢后處理。 Spring JPA 返回的對象仍然是(例如)一個 Team,包含一個玩家列表,包含一個團隊,包含一個玩家列表等等。
我想知道是否有辦法告訴 JPA 或存儲庫(或任何東西)不要一遍又一遍地綁定實體內的實體?
這是我在我的項目中處理這個問題的方法。
我使用了數據傳輸對象的概念,在兩個版本中實現:完整對象和輕對象。
我將一個包含引用實體的對象定義為 List 作為Dto
(僅保存可序列化值的數據傳輸對象),並將一個沒有引用實體的對象定義為Info
。
Info
對象僅保存有關實體本身的信息,而不保存有關關系的信息。
現在,當我通過 REST API 交付Dto
對象時,我只需為引用放置Info
對象。
假設我通過GET /players/1
PlayerDto
:
public class PlayerDto{
private String playerName;
private String playercountry;
private TeamInfo;
}
而TeamInfo
對象看起來像
public class TeamInfo {
private String teamName;
private String teamColor;
}
與TeamDto
相比
public class TeamDto{
private String teamName;
private String teamColor;
private List<PlayerInfo> players;
}
這避免了無休止的序列化,也為你的休息資源做一個合乎邏輯的結束,因為其他明智的你應該能夠GET /player/1/team/player/1/team
此外,該概念清楚地將數據層與客戶端層(在本例中為 REST API)分開,因為您沒有將實際的實體對象傳遞給接口。 為此,您將服務層內的實際實體轉換為Dto
或Info
。 我為此使用http://modelmapper.org/ ,因為它非常簡單(一個簡短的方法調用)。
我也懶惰地獲取所有引用的實體。 我的服務方法獲取實體並將其轉換為Dto
以在事務范圍內運行,這無論如何都是很好的做法。
要告訴 JPA 延遲獲取實體,只需通過定義獲取類型來修改關系注釋。 這個默認值是fetch = FetchType.EAGER
這在你的情況下是有問題的。 這就是為什么您應該將其更改為fetch = FetchType.LAZY
public class TeamEntity {
@OneToMany(mappedBy = "team",fetch = FetchType.LAZY)
private List<PlayerEntity> members;
}
同樣是Player
public class PlayerEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pla_fk_n_teamId")
private TeamEntity team;
}
從服務層調用存儲庫方法時,重要的是,這是在@Transactional
范圍內發生的,否則,您將無法獲得延遲引用的實體。 看起來像這樣:
@Transactional(readOnly = true)
public TeamDto getTeamByName(String teamName){
TeamEntity entity= teamRepository.getTeamByName(teamName);
return modelMapper.map(entity,TeamDto.class);
}
您可以使用@JsonIgnoreProperties注釋來避免無限循環,如下所示:
@JsonIgnoreProperties("members")
private Team team;
或者像這樣:
@JsonIgnoreProperties("team")
private List<Player> members;
或兩者兼而有之。
就我而言,我意識到我不需要雙向(一對多-多對一)關系。
這解決了我的問題:
// Team Class:
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Player> members = new HashSet<Player>();
// Player Class - These three lines removed:
// @ManyToOne
// @JoinColumn(name = "pla_fk_n_teamId")
// private Team team;
Project Lombok也可能會產生此問題。 如果您使用的是 Lombok,請嘗試添加@ToString
和@EqualsAndHashCode
。
@Data
@Entity
@EqualsAndHashCode(exclude = { "members"}) // This,
@ToString(exclude = { "members"}) // and this
public class Team implements Serializable {
// ...
這是關於無限遞歸注釋的很好指南https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.