I have 2 entities, one is Student and the other is Course. They are connected to each other in a manytomany bidirectional manner. I am performing an operation as seen in the code below.
@NoArgsConstructor
@AllArgsConstructor
@Entity
@SuperBuilder
@Table(name = "_student")
@DiscriminatorValue("ent_Student")
public class Student extends User{
@JsonIgnore
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "students_courses",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses;
public Set<Course> getCourses() {
if(courses == null) {
courses = new HashSet<Course>();
}
return courses;
}
public void setCourses(Set<Course> courses) {
this.courses = courses;
}
}
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "_course")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer id;
@Column(unique = true)
public String courseName;
@JsonIgnore
@ManyToMany(mappedBy = "courses")
public Set<Student> students;
//getter setters
}
and i have @Service method like this
public AssignCourseToStudentResponse assignCourseToStudent2(Integer id,Integer id2) {
AssignCourseToStudentResponse resp;
Optional<Student> st = student_Repo.findById(id);
Optional<Course> course = course_repo.findById(id2);
Student s = st.orElseThrow();
s.getCourses().add(course.get());
student_Repo.save(st.get());
Optional<Course> courseDebug = course_repo.findById(id2);
Iterator<Student> it= courseDebug.get().students.iterator();
while(it.hasNext()) {
System.out.println(it.next());
if(it.hasNext() == false) {
break;
}
}
resp = AssignCourseToStudentResponse.builder().message(ResponseMessage.SUCCESSFUL).build();
return resp;
}
when i execute the code written in the service section above
it is added to table as i expected.
But my real question is, if I delete cascade.persist from student, it adds student and course to the intermediate table just as before and creates a relationship between them.
So, what is the purpose of persist in this case, what does hibernate do in the background? Another question I have is that I just added a course to the student object and when I save the student directly, I can access my student object from the course entity, When I save only for the student object, how was my student added to the Student collection in the course object? Isn't this a cascade situation? Or how does hibernate work in this case? I'm trying to learn software. Could you please explain it in a little more detail. Thank you
I wasn't expecting data to be added to the intermediate table when cascade.persist was not present
The cascade
property in a @ManyToMany
annotation is a configuration option that allows the operations specified in the cascade property to be applied to both associated entities. In other words, operations made on your entity will also be made on the other side of the relationship.
CascadeType.PERSIST
means that if we save the Student
entity, then all Course
entities should also be saved. This cascade operation flows from the Student
object(s) to the related Course
object(s). However, since the relationship between Student
and Course
is managed in a separate association table (students_courses
), CascadeType.Persist
doesn't make any sense in the context of @ManyToMany
.
This is because Course
entities are not really "owned" by Student
entities in your setup. They exist independently, and are merely associated. Adding a Course
does not mean saving a new Course
in the database.
When you do s.getCourses().add(course.get());
, and save the student with student_Repo.save(st.get());
, You are not actually persisting a new Course
, but you are marking an association between an existing Student
and Course
, which is reflected in the join table(students_courses
).
Now if you check the Course
entity, you're able to get the associated Student
because you fetched the Course
again from the database with course_repo.findById(id2);
. Hibernate, then, fetches the relationship too from the students_courses
which has been updated previously when you added the Course
into the Student
.
In conclusion, Cascade.Persist
has little meaning in context of ManyToMany
relationships as persisting an entity doesn't mean persisting the relationship, and the added course is an associated entity, not a direct part of the Student
. The actual persistence of the relationship occurs when you save the Student
.
Hibernate handles this by saving the association in students_courses
table when you call save()
on Student
, and it fetches associations when you retrieve the Course
, giving the illusion as if it has been cascaded.
Remember CascadeType.PERSIST
is more significant in @OneToMany
and @OneToOne
relationships where persisting an entity might require persisting its children/descendants. In @ManyToMany
relationships, it doesn't persist the associated entities, just marks their association in the join table.
I hope this helps to clarify the concepts!