簡體   English   中英

由於雙向@ManyToMany 關系 SpringDataJPA 上的堆棧溢出錯誤,我無法創建實體或獲取實體列表

[英]I am unable to create entity or fetch list of entities due to stack overflow error on bi-directional @ManyToMany relationship SpringDataJPA

我正在開發一個springboot應用程序。 我有 2 個實體類,組和用戶。 我還在組 class(擁有實體)和用戶 class 中定義了 @ManyToMany 關系,這樣我就可以獲取用戶所屬的所有組。 不幸的是,由於以下錯誤,我無法創建新組或新用戶;

{
    "timestamp": "2022-09-09T20:29:22.606+00:00",
    "status": 415,
    "error": "Unsupported Media Type",
    "message": "Content type 'application/json;charset=UTF-8' not supported"
}

當我嘗試通過調用user.get().getGroups(); 我得到一個堆棧溢出錯誤

注意:目前我在 Group 和 User 類中分別有 @JsonManagedReference 和 @JsonBackReference 。 我也嘗試在兩個類上添加@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") ,但這也不起作用。 如下所示,將 value 參數添加到 @JsonManagedReference 和 @JsonBackReference 也不起作用。 我究竟做錯了什么? 我錯過了什么?

這是我的集團實體 class

@Table(name = "`group`") // <- group is a reserved keyword in SQL
public class Group {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @JsonView(Views.Public.class)
    private String name;
    private Integer maximumMembers;

    @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
    @JoinTable(name = "group_user", joinColumns = @JoinColumn(name = "group_id"), inverseJoinColumns = @JoinColumn(name = "user_id"))
    @JsonView(Views.Public.class)
    @JsonManagedReference(value = "group-member")
    private Set<User> groupMembers;
}

這是我的用戶實體 class

public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @JsonView(Views.Public.class)
    private Long id;
    @JsonView(Views.Public.class)
    private String nickname;
    @JsonView(Views.Public.class)
    private String username; // <- Unique user's phone number
    private String password;

    @ElementCollection(targetClass = ApplicationUserRole.class)
    @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
    @Enumerated(EnumType.STRING)
    @Column(name = "role")
    private Set<ApplicationUserRole> roles;

    @ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
    @JsonBackReference(value = "user-group")
    private Set<Group> groups;
}

最小的、可重現的示例https://github.com/Java-Techie-jt/JPA-ManyToMany

我找到了這個問題的永久解決方案。 對於其他面臨類似問題的人,這就是我發現的。 首先,我的實體類有@Data Lombok 注釋。 我刪除了這個,因為@Data注釋幾乎總是加載 collections ,即使你有FetchType.LAZY

您可以在此處閱讀更多關於為什么不應該使用@Data注釋您的實體 class https://www.jpa-buddy.com/blog/lombok-and-jpa-what-may-go-wrong/

刪除此注釋后,我從關系的雙方(兩個實體)中刪除@JsonManagedReference@JsonBackReference 然后我將@Jsonignore添加到僅引用端(用戶類)。 這解決了兩件事

  1. 創建一個包含用戶列表的組可以正常工作
  2. 將用戶列表添加到組中可以正常工作。

在此之后,我們剩下最后一個問題。 當我們嘗試從 api 讀取用戶時,我們得到的用戶沒有他們所屬的組的關聯列表,因為我們在用戶列表中有@JsonIgnore 為了解決這個問題,我讓 controller 返回一個新的 object。 所以在從我的服務中獲取用戶之后,我將 map 它轉移到一個新的數據傳輸 object 中,我在 Z5904C103F2C06E1 中返回這個 object。

從這里我使用@JsonView過濾我的回復。

這就是我的類的外觀,注意注釋中沒有@Data

團體

@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Getter
@Setter
@Table(name = "`group`") // <- group is a reserved keyword in SQL
public class Group {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
    private Integer maximumMembers;

    @ManyToMany(fetch = FetchType.EAGER,
            cascade = {CascadeType.MERGE, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinTable(name = "group_user",
            joinColumns = @JoinColumn(name = "group_id"),
            inverseJoinColumns = @JoinColumn(name = "user_id"))
    @JsonView(UserViews.PublicUserDetails.class)
    private Set<User> groupMembers = new HashSet<>();

} 

用戶

@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Getter
@Setter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @JsonView(UserViews.PublicUserDetails.class)
    private Long id;
    @JsonView(UserViews.PublicUserDetails.class)
    private String nickname;
    @JsonView(UserViews.PublicUserDetails.class)
    private String username; // <- Unique user's phone number
    private String password;

    @ElementCollection(targetClass = ApplicationUserRole.class)
    @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
    @Enumerated(EnumType.STRING)
    @Column(name = "role")
    @JsonView(UserViews.PublicUserDetails.class)
    private Set<ApplicationUserRole> roles;

    @JsonIgnore
    @ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
    private Set<Group> groups = new HashSet<>();
}

在用戶 controller 中獲取用戶的方法

@GetMapping("/get-groups")
    public ResponseEntity<UserRequestResponseDTO> getWithGroups(@RequestParam(name = "userId") Long userId) {
        User user = userService.getWithGroups(userId);
        UserRequestResponseDTO response = UserRequestResponseDTO.builder()
                .nickname(user.getNickname())
                .username(user.getUsername())
                .groups(user.getGroups())
                .build();
        return ResponseEntity.ok().body(response);
    }

希望這可以幫助某人

暫無
暫無

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

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