Search code examples
javahibernatecascadepersist

cascade.Persist in @ManyToMany how does it work generally and how does it work in this situation


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


Solution

  • 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!