gme*_*exo 3 recursion spring hibernate jpa
当我有用户并且他们有其他用户作为朋友时,我想制作类似于 facebook 的应用程序。所以我做了一个User与自己有 ManyToMany 关系的实体,它们也可以互相邀请到朋友列表。不幸的是,当我想获得邀请好友的用户时,我收到此错误:
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
Run Code Online (Sandbox Code Playgroud)
我缩短的用户实体类:
@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;
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我试图让它变得懒惰,我也尝试了 @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"));
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
为了避免无限递归,您应该只使用@JsonIgnoreProperties注释但使用所有嵌套多对多字段的数组,例如:
@JsonIgnoreProperties({"friends", "friendsOf", "invitedFriends", "invitedFriendsOf"})
@ManyToMany
@JoinTable(...)
private Set<Person> friends;
Run Code Online (Sandbox Code Playgroud)
然后,为了避免com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role...在您尝试在控制器中获取 Person 数据时出现的异常,您可以@EntityGraph(在存储库的查询方法上)使用attributePaths设置为这些字段名称数组的参数来填充它们一个查询中的值:
@Transactional(readOnly = true)
public interface PersonRepo extends JpaRepository<Person, Long> {
@EntityGraph(attributePaths = {"friends", "friendsOf", "invitedFriends", "invitedFriendsOf"})
Optional<Person> getById(Long aLong);
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,将设置所有字段值,避免递归,您将能够在控制器中获得正确的结果:
@GetMapping("/{id}")
public Person get(@PathVariable Long id) {
return personRepo.getById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Person not found"));
}
Run Code Online (Sandbox Code Playgroud)
那么你可能想要得到所有的人。考虑到单个人的数据相当大,将所有相关联的朋友的所有人都放在一个列表中是不正确的。最好只获取每个 Person 的基本字段。在这种情况下,您可以使用简单的 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();
}
}
Run Code Online (Sandbox Code Playgroud)
并将 Person 映射到它:
@GetMapping
public List<PersonDto> getAll() {
return personRepo.findAll().stream().map(PersonDto::new).collect(Collectors.toList());
}
Run Code Online (Sandbox Code Playgroud)
由于这种映射,您也将避免异常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;
}
Run Code Online (Sandbox Code Playgroud)
我的工作演示- 您可以在 IDE 中运行它,连接到 H2 数据库(使用这种方法)以查看其数据。如果您的 IDE 是 IntelliJ IDEA,您可以直接从文件demo.http运行演示请求。感谢log4jdbc-spring-boot-starter,您可以在应用程序日志中看到所有 SQL 查询。
| 归档时间: |
|
| 查看次数: |
1337 次 |
| 最近记录: |