JPA : OneToMany relationship keeps the Collection empty
It seems difficult for me to understand JPA and how OneToMany relationships actually work.
For example, suppose I have an object Class
@Entity
public class Class {
@Id
private String className;
@OneToMany(cascade = Cascade.ALL, orphanRemoval = true)
private Set<Student> students;
Constructors, Getters, Setter
}
I also have an object, Student
, which contains Class.
@Entity
public class Student {
@Id
private String studentName;
@ManyToOne
private Class class;
Constructors, Getters, Setter
}
Obviously, a student can have multiple classes, but leave this alone.
Why does findAll()
on the ClassRepository
return a set of empty students when I create a class and then use that class to create several students.
Class class = new Class("CS", new HashSet<>());
classRepository.save(class); repository has no special functions
Student student1 = new Student("1", class);
Student student2 = new Student("2", class);
studentRepository.save(student1);
studentRepository.save(student2);
classRepository.findAll() // Returns me a List<Class> with only one class object that has an empty set.
I
was thinking the above code should automatically see that the two students are from that class, so when I call buildingRepository.findAll(),
it returns a Class
object that correctly populates the student set.
So did I misunderstand? Or is my code wrong? How do I change it to fix it?
Solution
You can choose:
1。 One-way @OneToMany
:
@Entity
public class Class {
@Id
private String className;
@OneToMany(cascade = Cascade.ALL, orphanRemoval = true)
private List<Student> students=new ArrayList<>();
Constructors, Getters, Setter
}
@Entity
public class Student {
@Id
private String studentName;
Constructors, Getters, Setter
}
Now, if we stick to a Class
:
Class class1=new Class("name1");
class1.getStudents().add(new Student("student1Name"));
then you can make a save of class1 in db
classRepository.save(class);
2。 One-way @OneToMany
with @JoinColumn
:
To solve the extra join table problem mentioned earlier, we just need to add @JoinColumn
: to the mix
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "class_id")
private List<Student> students = new ArrayList<>();
3。 Bidirectional @OneToMany
:
The best way to map @OneToMany associations is to rely on the @ManyToOne side to propagate all entity state changes:
@Entity
public class Class {
@Id
private String className;
@OneToMany(
mappedBy = "class",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Student> students=new ArrayList<>();
Constructors, Getters, Setter
public void addStudent(Student student) {
students.add(student);
student.setClass(this);
}
public void removeStudent(Student student) {
students.remove(student);
student.setClass(null);
}
}
@Entity
public class Student {
@Id
private String studentName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "class_id")
private Class class;
}
And insist:
Class c1=new Class("className1");
c1.addStudent(new Student("StudentNAme1"));
c1.addStudent(new Student("StudentNAme2"));
c1.addStudent(new Student("StudentNAme3"));
classRepository.save(c1);