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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With