【问题标题】:Hibernate infinite loop recursion in many to many relation多对多关系中的休眠无限循环递归
【发布时间】:2018-02-01 20:24:58
【问题描述】:

我想创建双向的学生-学科关系。在我为用户注册纪律之前,一切正常。现在我得到了无限递归。

类看起来像这样:

//Student.java
@Entity
@Table(name = "students")
public class Student {
    @NotNull
    @Id
    @Column(name = "STUDENT_ID")
    private String id;
    private String name;
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "students_disciplines", joinColumns = @JoinColumn(name = "STUDENT_ID"), inverseJoinColumns = @JoinColumn(name = "DISCIPLINE_ID"))
    @JsonSerialize(using = NestedDisciplineSetSerializer.class)
    private Set<Discipline> disciplines = new HashSet<>();
    //Getters, Setters
}
//NestedDisciplineSetSerializer.java
public class NestedDisciplineSetSerializer extends JsonSerializer<Set<Discipline>> {
    @Override
    public void serialize(Set<Discipline> value, JsonGenerator jgen, SerializerProvider p) throws IOException, JsonProcessingException {
        jgen.writeStartArray();
        for (Discipline s : value) {
            jgen.writeStartObject();
            jgen.writeStringField("name", s.getName());
            jgen.writeNumberField("id", s.getId());
            jgen.writeBooleanField("recommended", s.isRecommended());
            jgen.writeEndObject();
        }
        jgen.writeEndArray();
    }
}
//Discipline.java
@Entity`enter code here`
@Table(name = "disciplines")
public class Discipline {
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    @Column(name = "DISCIPLINE_ID")
    private int id;
    @NotNull
    private String name;
    @NotNull
    private int credits;
    private String annotation;
    private boolean recommended;
    @ManyToMany(mappedBy = "disciplines", fetch = FetchType.EAGER)
    @JsonSerialize(using = NestedStudentSetSerializer.class)
    private Set<Student> students = new HashSet<>();
    //Getters, Setters
}
//NestedStudentSetSerializer.java
public class NestedStudentSetSerializer extends JsonSerializer<Set<Student>> {
    @Override
    public void serialize(Set<Student> value, JsonGenerator jgen, SerializerProvider p) throws IOException, JsonProcessingException {
        jgen.writeStartArray();
        for (Student s : value) {
            jgen.writeStartObject();
            jgen.writeStringField("name", s.getName());
            jgen.writeStringField("id", s.getId());
            jgen.writeEndObject();
        }
        jgen.writeEndArray();
    }
}

Student s = sDao.findOne(id); 之后加载 Set 时出现错误

试图在这里搜索,但无法找到正确的解决方案。任何事情都会产生相同的结果。

Error log

【问题讨论】:

    标签: java hibernate recursion many-to-many


    【解决方案1】:

    org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:430) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 在 truelecter.practproekt.entity.Discipline.hashCode(Discipline.java:31) 〜[类/:na] 在 java.util.HashMap.hash(HashMap.java:338) ~[na:1.8.0_91] 在 java.util.HashMap.put(HashMap.java:611) ~[na:1.8.0_91] 在 java.util.HashSet.add(HashSet.java:219) ~[na:1.8.0_91] 在 java.util.AbstractCollection.addAll(AbstractCollection.java:344) ~[na:1.8.0_91] 在 org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:327) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 在 org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:234) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 在 ... org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:430) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final] 在 truelecter.practproekt.entity.Student.hashCode(Student.java:29) ~[classes/:na] 在 java.util.HashMap.hash(HashMap.java:338) ~[na:1.8.0_91] 在 java.util.HashMap.put(HashMap.java:611) ~[na:1.8.0_91] 在 java.util.HashSet.add(HashSet.java:219) ~[na:1.8.0_91] 在 java.util.AbstractCollection.addAll(AbstractCollection.java:344) ~

    我的印象是,DisciplineStudent 实体的 hashcode() 方法实现同时使用了这两个实体。
    也许我错了,在堆栈跟踪中不是很清楚。
    您如何为您的实体定义 equals()hashcode() 方法?
    假设实体 A 和实体 B 具有双向关系。
    如果equals()/hashcode()实现实体A 在实体B 字段上调用equals()/hashcode(),它本身在实体A 字段上调用equals()/hashcode(),你有一个循环。
    而你的错误。

    此外,即使您通过确保在equals()/hashcode()/toString() 中只有一个引用另一个来缩短循环,您也应该谨慎地调用实体关系的equals()/hashcode()/toString 方法。
    它可能会对性能产生实际影响。如果打开 Hibernate 会话,它可能确实会执行您不希望执行的查询。

    【讨论】:

    • 感谢您的回答,问题出在 lombok 库中,我用它来简化 getter 和 setter 的生成。这是 @Data 注释为创建循环的两个实体生成的 hashCode() 方法。
    • 在实现 equals() 和 hashcode() 并删除彼此的引用后,它起作用了!!!我学到了一些东西,tks!!!
    【解决方案2】:

    双方都有 Eager fetch 类型。这意味着对于您加载的每个学生,Hibernate 将加载每个相关的学科,并且对于每个学科,它将加载其学生。这是你的循环。对于FetchType.LAZY,您可以更改一侧的获取类型(两者都更好)。这样做,学生的学科将在您访问列表时加载。

    【讨论】:

      猜你喜欢
      • 2018-12-23
      • 2020-04-14
      • 2021-12-23
      • 2020-11-01
      • 2019-12-27
      • 2015-12-22
      相关资源
      最近更新 更多