Search code examples
javahibernaterecursionmany-to-many

Hibernate infinite loop recursion in many to many relation


I want to create bidirectional student-discipline relation. Anything works fine until I've enrolled user to discipline. Now I'm getting infinite recursion.

Classes looks like this:

//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();
    }
}

Error appears to be while loading Set after Student s = sDao.findOne(id);

Tried to search here, but was not able to find right solution. Anything made the same result.

Error log


Solution

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

    I have the impression that the hashcode() method implementations for Discipline and Student entities use both entities.
    Maybe am I wrong, it is not very clear in the stacktrace.
    How have you defined equals() and hashcode() methods for your entities ?
    Suppose entity A and entity B with a bidirectional relationship.
    If equals()/hashcode() implementation of entity A invokes equals()/hashcode() on the entity B field that itself invokes equals()/hashcode() on the entity A field, you have a cycle.
    Whereas your error.

    Besides, even if you cut the cycle by ensuring that only one of both refers the other in equals()/hashcode()/toString(), you should invoke equals()/hashcode()/toString methods of the entity relationships with cautious.
    It may have a real impact on the performance. If the Hibernate session is opened, it may indeed execute queries that you don't expect to.