Tima
Tima

Reputation: 12905

Hibernate. How to map two many-to-many to the same entity

I just try to find out how to map following situation in Hibernate.

There are some courses and some students. Any student has its own study plan containing optional courses and required courses.

It looks easy for me to model this in the database world. I would have four tables:

  1. Course (id, name, description)
  2. Student (id, name)
  3. student_course_optional (student_id, course_id)
  4. student_course_required (student_id, course_id)

But I have not really an idea how to map this with hibernate.

Here my first draft:

@Entity
public class Student {

    private Long id;
    private Long version;
    private String name;
    private List<Course> requiredCourses;
    private List<Course> optionalCourses;

    ...

    @ManyToMany
    @JoinTable(name="Course")
    public List<Course> getRequiredCourses() {
        return requiredCourses;
    }

    @ManyToMany
    @JoinTable(name="Course")
    public List<Course> getOptionalCourses() {
        return optionalCourses;
    }
}

@Entity
public class Course {

    private Long id;
    private Long version;
    private String name;
    private List<Student> students;
    private List<Student> optionalStudents;

    ...

    @ManyToMany(mappedBy="requiredCourses")
    public List<Course> getStudents() {
        return requiredCourses;
    }

    @ManyToMany(mappedBy="optionalCourses")
    public List<Course> getOptionalStudents() {
        return optionalCourses;
    }
}

But somehow this looks weird for me. Or is this correct?

Upvotes: 0

Views: 203

Answers (2)

wypieprz
wypieprz

Reputation: 8219

An example implementation may use an intermediate entity, let's say StudyPlan, which acts as UML association class. Such entity consists of:

  • the compound primary key represented by StudyPlanId class
  • the foreign key represented by student and course fields (in JPA terms this is a derived identifier)
  • additional state represented by optional field (it allows to distinguish between required and optional course)
@Entity
@IdClass(StudyPlanId.class)
public class StudyPlan {
    @Id
    @ManyToOne
    private Student student;

    @Id
    @ManyToOne
    private Course course;

    private boolean optional;
    ...
}
public class StudyPlanId implements Serializable {
    private int course;
    private int student;

    @Override
    public int hashCode() { ... }
    @Override
    public boolean equals(Object obj) { ... }
    ... 
}
@Entity
public class Student {
    @Id
    private int id;

    @OneToMany(mappedBy = "student")
    private Collection<StudyPlan> plan;

    private String name;
    ...
}
@Entity
public class Course {
    @Id
    private int id;

    @OneToMany(mappedBy = "course")
    private Collection<StudyPlan> plan;

    private String name;
    private String description;
    ...     
}

The above entity model will produce the following data model:

Student            StudyPlan            Course
===============    =================    ===============
id           PK    student_id  FK PK    id           PK
name               course_id   FK PK    name      
                   optional             description

Upvotes: 1

Andy Ying
Andy Ying

Reputation: 22

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "student_course_required", joinColumns = @JoinColumn(name = "student_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "course_id", referencedColumnName = "id"))
private List<Course> requiredCourses= new ArrayList<Course>();

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "student_course_optional", joinColumns = @JoinColumn(name = "student_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "course_id", referencedColumnName = "id"))
private List<Course> optionalCourses= new ArrayList<Course>();

Public Class Studentneed change to up code.

 @ManyToMany(fetch = FetchType.EAGER)
 @JoinTable(name = "student_course_required", joinColumns = @JoinColumn(name = "course_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "student_id", referencedColumnName = "id"))
 private List<Student> requiredStudents= new ArrayList<Student>();

 @ManyToMany(fetch = FetchType.EAGER)
 @JoinTable(name = "student_course_optional", joinColumns = @JoinColumn(name = "course_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "student_id", referencedColumnName = "id"))
 private List<Student> optionalStudents= new ArrayList<Student>();

public Class Courseneed change to up code. I think this can solve your question.

Upvotes: 0

Related Questions