gpt4 book ai didi

spring - 同一实体中多对多关系的无限递归

转载 作者:行者123 更新时间:2023-12-04 00:21:57 24 4
gpt4 key购买 nike

当我有用户并且他们有其他用户作为 friend 时,我想制作类似于 facebook 的应用程序。所以我做了一个实体User,它与自身有ManyToMany关系,他们也可以互相邀请到 friend 列表中。不幸的是,当我想获取邀请 friend 的用户时出现此错误:

Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.pk.thesis.devbook.models.dto.UserDTO["invitedFriends"]->java.util.ArrayList[0]->com.pk.thesis.devbook.models.dto.UserDTO["invitedFriends"]->java.util.ArrayList[0]-
... (it goes forever)
>com.pk.thesis.devbook.models.dto.UserDTO["invitedFriends"]->java.util.ArrayList[0]with root cause

我缩短的用户实体类:

    @Data
@Entity
@Table( name = "users",
uniqueConstraints = {
@UniqueConstraint(columnNames = "username"),
@UniqueConstraint(columnNames = "email")
})
@JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="@id")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotBlank
@Size(max = 40)
private String username;

//other things...

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name="tbl_friends",
joinColumns=@JoinColumn(name="personId"),
inverseJoinColumns=@JoinColumn(name="friendId")
)
private List<User> friends;

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(name="tbl_friends",
joinColumns=@JoinColumn(name="friendId"),
inverseJoinColumns=@JoinColumn(name="personId")
)
private List<User> friendOf;

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(name="tbl_invites_to_friends",
joinColumns=@JoinColumn(name="personId"),
inverseJoinColumns=@JoinColumn(name="invited_personId")
)
@JsonIgnoreProperties("invitationsToFriends")
private List<User> invitedFriends;

@JsonIgnore
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(name="tbl_invites_to_friends",
joinColumns=@JoinColumn(name="invited_personId"),
inverseJoinColumns=@JoinColumn(name="personId")
)
@JsonIgnoreProperties("invitedFriends")
private List<User> invitationsToFriends;
}

如你所见,我试图让它变得懒惰,我也尝试了 @JsonIgnore 注释,但没有任何效果。有什么建议吗?

我的返回 UserDTO 的方法(将 User 映射到 UserDTO)

public UserDTO getUserDTO(String username) {
return userRepository.findByUsername(username)
.map(u -> modelMapper.map(u, UserDTO.class))
.orElseThrow(() -> new UsernameNotFoundException("User not
found"));
}

UserDTO 通过 org.modelmapper.ModelMapper 映射

public class UserDTO {

private String username;
private String firstname;
private String lastname;
private String email;
private List<UserDTO> invitedFriends;
private List<UserDTO> invitationsToFriends;
}

最佳答案

为避免无限递归,您应该只使用 @JsonIgnoreProperties 注释,但使用所有嵌套的多对多字段的数组,例如:

@JsonIgnoreProperties({"friends", "friendsOf", "invitedFriends", "invitedFriendsOf"})
@ManyToMany
@JoinTable(...)
private Set<Person> friends;

然后,为了避免异常 com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role... 当您尝试在 Controller 中获取 Person 数据时,该异常会上升,您可以使用 @EntityGraph (在存储库的查询方法上)和参数 attributePaths 一起设置为这些字段名称的数组,以填充它们的值一个查询:

@Transactional(readOnly = true)
public interface PersonRepo extends JpaRepository<Person, Long> {
@EntityGraph(attributePaths = {"friends", "friendsOf", "invitedFriends", "invitedFriendsOf"})
Optional<Person> getById(Long aLong);
}

在这种情况下,将设置所有字段值,避免递归,您将能够在 Controller 中获得正确的结果:

@GetMapping("/{id}")
public Person get(@PathVariable Long id) {
return personRepo.getById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Person not found"));
}

那么你可能想要得到所有的人。考虑到一个人的数据非常大,将所有相关联的 friend 都放在一个列表中是不正确的。最好只获取每个人的基本字段。在这种情况下,您可以使用简单的 DTO:

@Value
public class PersonDto {
private long id;
private String name;
private String email;

public PersonDto(Person person) {
this.id = person.getId();
this.name = person.getName();
this.email = person.getEmail();
}
}

并将 Person 映射到它:

@GetMapping
public List<PersonDto> getAll() {
return personRepo.findAll().stream().map(PersonDto::new).collect(Collectors.toList());
}

由于这种映射,您也将避免 com.fasterxml.jackson.databind.JsonMappingException 异常。


此答案中使用的实体人:

@Data
@EqualsAndHashCode(of = "email")
@ToString(of = {"id", "name", "email"})
@Entity
@Table(name = "people")
public class Person {
@Id
@GeneratedValue
private Long id;

@Column(nullable = false, length = 32)
private String name;

@NaturalId
@Column(nullable = false, length = 32)
private String email;

@JsonIgnoreProperties({"friends", "friendsOf", "invitedFriends", "invitedFriendsOf"})
@ManyToMany
@JoinTable(name = "friends", joinColumns = @JoinColumn(name = "person_id"), inverseJoinColumns = @JoinColumn(name = "friend_id"))
private Set<Person> friends;

@JsonIgnoreProperties({"friends", "friendsOf", "invitedFriends", "invitedFriendsOf"})
@ManyToMany
@JoinTable(name = "friends", joinColumns = @JoinColumn(name = "friend_id"), inverseJoinColumns = @JoinColumn(name = "person_id"))
private Set<Person> friendsOf;

@JsonIgnoreProperties({"friends", "friendsOf", "invitedFriends", "invitedFriendsOf"})
@ManyToMany
@JoinTable(name = "invited_friends", joinColumns = @JoinColumn(name = "person_id"), inverseJoinColumns = @JoinColumn(name = "friend_id"))
private Set<Person> invitedFriends;

@JsonIgnoreProperties({"friends", "friendsOf", "invitedFriends", "invitedFriendsOf"})
@ManyToMany
@JoinTable(name = "invited_friends", joinColumns = @JoinColumn(name = "friend_id"), inverseJoinColumns = @JoinColumn(name = "person_id"))
private Set<Person> invitedFriendsOf;
}

My working demo - 你可以在你的 IDE 中运行它,连接到 H2 数据库(使用 this approach )来查看它的数据。如果您的 IDE 是 IntelliJ IDEA,您可以直接从文件 demo.http 运行演示请求.感谢 log4jdbc-spring-boot-starter您可以在应用程序日志中看到所有 SQL 查询。

关于spring - 同一实体中多对多关系的无限递归,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59877052/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com