Hibernate:无限递归,如何解决?

时间:2018-04-24 10:05:14

标签: java spring rest

我的无限递归问题非常烦人。我的应用程序正在构建为Spring Rest API,我使用Lombok生成构造函数和getter以及setter。我的模特很少。

AppUser - 可能是最大的一个。

@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
public class AppUser {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String login;
    private String name;
    private String surname;
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String password;

    public AppUser(String login, String password, UserRole userRole) {
        this.login = login;
        this.password = password;
        this.userRoleSet = new HashSet<>();
        this.userRoleSet.add(userRole);
    }

    public AppUser(String login, String name, String surname, String password) {
        this.login = login;
        this.name = name;
        this.surname = surname;
        this.password = password;
    }

    @OneToMany(mappedBy = "appUser",fetch = FetchType.EAGER)
    private Set<UserRole> userRoleSet;

    @OneToMany(mappedBy = "createdBy")
    private List<Incident> createdIncidentsList;

    @OneToMany(mappedBy = "assignedTo")
    private List<Incident> assignedToIncidentsList;

    @OneToMany(mappedBy = "postedBy")
    private List<Comment> commentList;

    @OneToMany(mappedBy = "appUser")
    private List<IncidentChange> incidentChangeList;

}

事件

@Data
@Entity
public class Incident {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String description;
    private LocalDateTime creationDate;
    private IncidentStatus status;

    @OneToMany(mappedBy = "incident", cascade = CascadeType.ALL)
    private List<Comment> commentList;
    @ManyToOne
    private AppUser createdBy;
    @ManyToOne
    private AppUser assignedTo;
    @OneToOne(cascade = CascadeType.ALL)
    private ChangeLog changeLog;

    public Incident(String title, String description) {
        this.title = title;
        this.description = description;
        this.creationDate = LocalDateTime.now();
        this.status = IncidentStatus.NEW;
        this.commentList = new ArrayList<>();
        this.changeLog = new ChangeLog();
    }
    public Incident(){

    }
    public Incident(String title, String description, LocalDateTime creationDate, IncidentStatus status, List<Comment> commentList, AppUser assignedTo, AppUser createdBy, ChangeLog changeLog) {
        this.title = title;
        this.description = description;
        this.creationDate = creationDate;
        this.status = status;
        this.commentList = commentList;
        this.changeLog = changeLog;
    }

}

注释

@Data
@NoArgsConstructor
@Entity
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String description;
    private LocalDateTime creationDate;

    @ManyToOne
    private AppUser postedBy;
    @ManyToOne
    private Incident incident;

}

修改记录

@Data
@Entity
public class ChangeLog {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @OneToOne
    private Incident incident;
    @OneToMany(mappedBy = "changeLog")
    private List<IncidentChange> incidentChangeList;

    public ChangeLog(){
        this.incidentChangeList = new ArrayList<>();
    }

    public ChangeLog(Incident incident, List<IncidentChange> incidentChangeList) {
        this.incident = incident;
        this.incidentChangeList = incidentChangeList;
    }

}

IncidentChange

@Data
@NoArgsConstructor
@Entity
public class IncidentChange {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String description;
    private LocalDateTime changeDate;

    @ManyToOne
    private ChangeLog changeLog;
    @ManyToOne
    private AppUser appUser;
}

简而言之就是这样:

AppUser与事件,评论和事件变更有一个关系。

事件与AppUser有很多关系(TWICE很重要),
             OneToMany评论,
             OneToOne to ChangeLog

评论与AppUser有很多关系,
            ManyToOne事件发生

ChangeLog具有与事件的OneToOne关系,
              OneToMany to IncidentChange

IncidentChange具有与ChangeLog的ManyToOne关系,
                ManyToOne到AppUser

我知道AppUser处于事件状态,并且在Comment中嵌套在Incident中,与IncidentChange相同,但它的嵌套更深。

以下是想要检查如何创建AppUser和突发事件的人员的代码https://bitbucket.org/StabloPL/backendsimpleticketsystem/src/76e6bd107e68c82614fbc040b95f041fd7f51d28/src/main/java/com/djagiellowicz/ticketsystem/backendsimpleticketsystem/model/?at=master IncidentController,AppUserControler和IncidentService,AppUserService都是可能让人感兴趣的东西。

当我创建用户(通过Postman作为JSON),之后我获取它,没有问题一切正常。当我创建事件并将其分配给特定的人时,我无法获取事件,也无法获取此特定用户。其他页面,其中没有创建事件的用户获取没有任何问题。这同样适用于未分配createdBy用户的事件。一切都保存到数据库,没有任何问题。

当我尝试获取事件时,Hibernate / Spring抛出了这个错误。当然有点削减。

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
    at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:472) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129) ~[tomcat-embed-core-8.5.29.jar:8.5.29]


2018-04-24 11:39:43.731 ERROR 18300 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [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: com.djagiellowicz.ticketsystem.backendsimpleticketsystem.model.AppUser["createdIncidentsList"]->org.hibernate.collection.internal.PersistentBag[0]->com.djagiellowicz.ticketsystem.backendsimpleticketsystem.model.Incident["createdBy"]->com.djagiellowicz.ticketsystem.backendsimpleticketsystem.model.AppUser["createdIncidentsList"]->org.hibernate.collection.internal.PersistentBag[0]->
java.lang.StackOverflowError: null

我该如何解决?我必须知道AppUser发布了哪条评论,同样适用于Incident和IncidentChange,我不想从Incident / IncidentChange / Comment

中删除这些信息

2 个答案:

答案 0 :(得分:2)

您必须覆盖Lombok的注释...当您使用@Data时,您隐式使用类中的所有字段调用@EqualsAndHashCode。无限递归从您用于映射关系的字段中引发。每个类中@EqualsAndHashCode以排除集合字段。例如,您可以在AppUser类上添加annotatio @EqualsAndHashCode(exclude={"userRoleSet", "createdIncidentsList","assignedToIncidentsList","commentList","incidentChangeList"})以覆盖@Data默认策略。您必须在每个模型类上执行此操作,直到没有无限递归错误。

此外......您的类之间存在循环依赖关系。序列化对象时,应删除所有循环依赖项。最好的解决方案是序列化DTO而不是实体。创建没有循环依赖关系的DTO应该可以解决这个问题。在序列化/反序列化中,您可以使用包含事件列表的AppUserDTO,一个不带AppUser参考的IncidentDTO列表。

答案 1 :(得分:1)

您可以将@JsonIgnore注释放在您不希望包含在JSON中的字段中。

如果您想要更灵活,可以使用@JsonView注释。

您还可以使用@JsonManagedReference@JsonBackReference

您使用的是由您自己决定的。如果您真的不需要前端的属性,我建议您使用@JsonIgnore。如果在某种情况下您可能需要一个属性而不是另一个属性,我会使用@JsonView