当前位置: 首页 > 知识库问答 >
问题:

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

翟俊哲
2023-03-14

当我有用户并且他们有其他用户作为朋友时,我想让应用程序类似于facebook。因此,我创建了一个实体用户,该实体本身有很多关系,它们也可以邀请对方加入好友列表。不幸的是,当我想获取具有好友邀请的用户时,我遇到了此错误:

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

共有2个答案

申屠裕
2023-03-14

多亏了评论部分的答案,我找到了一种方法。我制作了另一个带有附加日期字段的实体InvitationsToFriend,并通过OneTo多国关系将其与我的用户实体连接起来。我还使用我需要的字段(用户名、名字、姓氏)创建了Reduce edUserDTO和Reduce edInvitationsToFriendsDTO。

我的用户类别:

@Entity
      public class User implements Serializable {
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private Long id;

            @Size(max = 40)
            private String username;

            @Size(max = 120)
            private String password;

            @Column
            private String firstname;

            @Column
            private String lastname;

            @OneToMany(mappedBy="to")
            private List<InvitationsToFriends> invitationsToFriends;

            @OneToMany(mappedBy="from")
            private List<InvitationsToFriends> invitedFriends;

    }

邀请朋友:

@Entity
    public class InvitationsToFriends implements Serializable{

        @Id
        @GeneratedValue(strategy= GenerationType.AUTO)
        private Long id;

        @ManyToOne( fetch = FetchType.LAZY)
        @JoinColumn(name="from_user_fk")
        private User from;

        @ManyToOne( fetch = FetchType.LAZY)
        @JoinColumn(name="to_user_fk")
        private User to;

        @Column(name = "invitation_date")
        private Date invitationDate;
    }

用户:

@Data
public class UserDTO {

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

将生命还原为朋友,并还原为:

@Data
public class ReducedInvitationsToFriendsDTO {

    private ReducedUserDTO from;
    private ReducedUserDTO to;
}

@Data
public class ReducedUserDTO {

    private String username;
    private String firstname;
    private String lastname;
}

现在,响应json如下所示:

    username: "username"
    firstname: "firstname"
    lastname: "lastname"
    email: "email@email.com"
    invitedFriends: [
from: {username: "username", firstname: "firstname", lastname: "lastname"}
to: {username: "invitedUsername", firstname: "invitedFirstname", lastname: "invitedLastName"}]
霍鸣
2023-03-14

要避免无限递归,只需使用注释,但要使用所有嵌套多对多字段的数组,例如:

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

然后,为了避免出现异常。fasterxml。杰克逊。数据绑定。JsonMappingException:未能延迟初始化角色的集合 将上升,您可以使用存储库的查询方法上的EntityGraph参数AttributePath,该参数也设置为这些字段名称的数组,在一个查询中填充它们的值:

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

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

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

然后你可能想要获取所有的人。考虑到一个人的数据非常大,在一个列表中获取所有相关朋友的所有人是不正确的。最好只获取每个人的基本字段。在这种情况下,您可以使用简单的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。杰克逊。数据绑定。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;
}

我的工作演示-您可以在IDE中运行它,连接到H2数据库(使用这种方法)以查看其数据。如果您的IDE是IntelliJ IDEA,则可以直接从demo文件运行demo请求。http。多亏了log4jdbc spring boot starter,您可以在应用程序日志中看到所有SQL查询。

 类似资料:
  • 问题内容: 另一个hibernate问题…:P 使用Hibernate的Annotations框架,我有一个实体。每个人都可以有一个朋友的集合:其他的集合。但是,我还无法弄清楚如何在由s 列表组成的类中创建多对多关联(使用用户友好中间表)。 这是User类及其注释: 用户- 朋友映射表只有两列,这两列都是该表列的外键。两列是(应映射到当前用户)和(指定当前用户的朋友的ID)。 问题是,即使我已经预

  • DBSchema-2表(用户和角色)具有多对多关系,并由postgres中的中间表(用户角色)桥接。我想获取所有角色和创建它的人的姓名。名称在用户表中可用,但所有其他详细信息在角色表中。Roles表中有一个由创建人(创建角色的人的用户id)创建的字段。 我正在尝试构建一个GET请求,以查看给定id的所有角色以及创建id的人的姓名 实体类Users1.java 实体类角色。JAVA 角色。存储库类

  • 问题内容: 问题在标题中: 如何将多对多关系表作为实体? 问题答案: 我会说,你的问题很合理。看看这个文档部分:第24章最佳实践。摘录: 不要使用外来关联映射: 真正的多对多关联的实际测试案例很少见。大多数时候,您需要存储在“链接表”中的其他信息。在这种情况下,最好将两个一对多关联用于中间链接类。实际上,大多数关联是一对多和多对一的。因此,在使用任何其他关联样式时,您应谨慎进行。 我们正在处理的方

  • 救命啊!我只在一对多关系数据库中插入POJO时遇到问题。我有POJO和误解如何插入数据库中的所有列表??? 请帮帮我伙计们!

  • 我在一个laravel控制器中运行以下代码, 但是我似乎从服务器上得到了以下错误, {“error”:{“type”:“ErrorException”,“message”:“preg_replace():参数不匹配,模式为字符串,而替换为数组”,“file”:“/Users/S/Sites/app api/vendor/laravel/framework/src/illumb/Support/he