16

I have these two class(table)

@Entity
@Table(name = "course")
public class Course {

    @Id
    @Column(name = "courseid")
    private String courseId;
    @Column(name = "coursename")
    private String courseName;
    @Column(name = "vahed")
    private int vahed;
    @Column(name = "coursedep")
    private int dep;
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "student_course", joinColumns = @JoinColumn(name = "course_id"),  inverseJoinColumns = @JoinColumn(name = "student_id"))
    private Set<Student> student = new HashSet<Student>();
//Some setter and getter

and this one:

    @Entity
    @Table(name = "student")
    public class Student {

        @Id
        @Column(name="studid")
        private String stId;
        @Column(nullable = false, name="studname")
        private String studName;
        @Column(name="stmajor")
        private String stMajor;
        @Column(name="stlevel", length=3)
        private String stLevel;
        @Column(name="stdep")
        private int stdep;

        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(name = "student_course"
                ,joinColumns = @JoinColumn(name = "student_id")
                ,inverseJoinColumns = @JoinColumn(name = "course_id")
        )
        private Set<Course> course = new HashSet<Course>();
//Some setter and getter

After running this code an extra table was created in database(student_course), now I wanna know how can I add extra field in this table like (Grade, Date , and ... (I mean student_course table)) I see some solution but I don't like them, Also I have some problem with them:

First Sample

Am1rr3zA
  • 7,115
  • 18
  • 83
  • 125
  • You can add one extra column using \@OrderColumn which is an int column used to store the sort order of a many2many relationship. I wish they would add a \@TempolalColumns to add a fromDate and toDate fields too – Neil McGuigan Aug 09 '13 at 06:43

4 Answers4

17

If you add extra fields on a linked table (STUDENT_COURSE), you have to choose an approach according to skaffman's answer or another as shown bellow.

There is an approach where the linked table (STUDENT_COURSE) behaves like a @Embeddable according to:

@Embeddable
public class JoinedStudentCourse {

    // Lets suppose you have added this field
    @Column(updatable=false)
    private Date joinedDate;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="STUDENT_ID", insertable=false, updatable=false)
    private Student student;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="COURSE_ID", insertable=false, updatable=false)
    private Course course;

    // getter's and setter's 

    public boolean equals(Object instance) {
        if(instance == null)
            return false;

        if(!(instance instanceof JoinedStudentCourse))
            return false;

        JoinedStudentCourse other = (JoinedStudentCourse) instance;
        if(!(student.getId().equals(other.getStudent().getId()))
            return false;

        if(!(course.getId().equals(other.getCourse().getId()))
            return false;

        // ATT: use immutable fields like joinedDate in equals() implementation
        if(!(joinedDate.equals(other.getJoinedDate()))
            return false;

        return true;
    }

    public int hashcode() {
        // hashcode implementation
    }

}

So you will have in both Student and Course classes

public class Student {

    @CollectionOfElements
    @JoinTable(
        table=@Table(name="STUDENT_COURSE"),
        joinColumns=@JoinColumn(name="STUDENT_ID")
    )
    private Set<JoinedStudentCourse> joined = new HashSet<JoinedStudentCourse>();

}

public class Course {

    @CollectionOfElements
    @JoinTable(
        table=@Table(name="STUDENT_COURSE"),
        joinColumns=@JoinColumn(name="COURSE_ID")
    )
    private Set<JoinedStudentCourse> joined = new HashSet<JoinedStudentCourse>();

}

remember: @Embeddable class has its lifecycle bound to the owning entity class (Both Student and Course), so take care of it.

advice: Hibernate team suppports these two approachs (@OneToMany (skaffman's answer) or @CollectionsOfElements) due some limitations in @ManyToMany mapping - cascade operation.

regards,

Arthur Ronald
  • 33,349
  • 20
  • 110
  • 136
  • great answer. if one only needs an index-column for mapping to a List one can just add @OrderColumn though – cproinger Aug 27 '12 at 14:14
  • 2
    When i tried above solution i got `Repeated column in mapping for collection` error. Any idea ? – Ankur Raiyani Nov 27 '13 at 10:56
  • I am getting the same error as Ankur Raiyani, my solution is the skaffman one. using embedded id within the studentcourse to make sure i have the right primary key going on for me. And then i removed the embedded key and threw one @Id for each manToOne relation – Eyad Ebrahim Feb 21 '15 at 14:51
  • 1
    @Ankur Raiyani Fixed. It occurs because 2 properties - Student.joined and JoinedStudentCourse.student - manage the same column. **Since Hibernate can manage only one property**, you should disable one property using *insertable=false, updatable=false*. The same is valid for Course.joined and JoinedStudentCourse.course – Arthur Ronald Jun 28 '16 at 19:13
  • @Eyad Ebrahim See reply to comment made by Ankur Raiyani – Arthur Ronald Jun 28 '16 at 19:14
7

The student_course table is there purely to record the association between the two entities. It is managed by hibernate, and can contain no other data.

The sort of data you want to record needs to be modelled as another entity. Perhaps you could a one-to-many association between Course and StudentResult (which contains the grade, etc), and then a many-to-one association between StdentResult and Student.

skaffman
  • 398,947
  • 96
  • 818
  • 769
3

Drop the many-to-many, create a class called StudentCourseRelationship and set up one to manys on Student and Course to the StudentCourseRelationship.

You can put all sorts of things on it, like DateEnrolled, DateKickedOut etc. etc.

IMO the many-to-many mapping is a bit of a con.

mcintyre321
  • 12,996
  • 8
  • 66
  • 103
2

The accepted answer unfortunately doesn't work for me, hibernate generates the join table in a weird way (all join columns are duplicated). However the variant with dedicated entity for the join table works fine. Here it is described in great detail: http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/

azerole
  • 1,262
  • 3
  • 16
  • 23