spring-data-jpa 由于双向@ManyToMany关系SpringDataJPA上的堆栈溢出错误,我无法创建实体或提取实体列表

zengzsys  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(127)

我正在使用一个springboot应用程序。我有两个实体类,Group和User。我还在Group类(Owning实体)和User类中定义了@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"),但也不起作用。如下所示,向@JsonManagedReference和@JsonBackReference添加值参数也不起作用。我做错了什么?我遗漏了什么?
这是我的Group实体类

@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;
}

这是我的User实体类

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

d8tt03nd

d8tt03nd1#

我找到了这个问题的永久解决方案。对于其他面临类似问题的人,这就是我的发现。首先,我的实体类有@Data Lombok注解。我删除了这个注解,因为@Data注解几乎总是加载集合,即使你有FetchType.LAZY
您可以阅读更多关于为什么不应该使用@Data来注解实体类的信息https://www.jpa-buddy.com/blog/lombok-and-jpa-what-may-go-wrong/
删除这个注解后,我从关系的两端(两个实体)删除了@JsonManagedReference@JsonBackReference,然后只在引用端(User类)添加了@Jsonignore
1.创建包含用户列表的组可以正常工作
1.将用户列表添加到组可以正常工作。
在这之后,我们剩下最后一个问题。当我们试图从api读取用户时,我们得到的用户没有他们所属的组的关联列表,因为我们在用户列表中有@JsonIgnore。为了解决这个问题,我让控制器返回一个新对象。所以在从我的服务中获取用户后,我将其Map到一个新的数据传输对象,然后我在控制器中返回这个对象。
在这里,我使用@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<>();
}

一种在用户控制器中获取用户的方法

@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);
    }

希望这能帮助到一些人💁

相关问题