Many-to-Many Using a Composite Key

studentstudent_coursecourse

id (PK) name

student_id (PK, FK) course_id (PK, FK) rating

id (PK) name

如上,现在我们希望在中间表加上一个 rating 属性,以表示某学生对某课程的评价。

很明显,上一小节谈到的 Basic Many-to-Many 是没有能力做到这一点的,简单地打上 @ManyToMany 注解并不能在中间表中加上自定义属性。

这就是为什么我们需要 Compositing Key (组合键)

Spring Data JPA 实现组合键

@Embeddable
@Getter
@Setter
class CourseRatingKey implements Serializable {
    @Column(name = "student_id")
    private Long studentId;

    @Column(name = "course_id")
    private Long courseId;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CourseRatingKey that = (CourseRatingKey) o;
        return Objects.equals(studentId, that.studentId) && Objects.equals(courseId, that.courseId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(studentId, courseId);
    }
}

一个组合键类必须满足

  • 标记为 @Embeddable

  • 实现 java.io.Serializable

  • 必须实现 equals()hashCode() 两个方法

  • 实例域中不能出现实体类型

使用组合键:

中间表:

@Entity
@Getter
@Setter
class CourseRating {
    @EmbeddedId
    private CourseRatingKey id;

    @ManyToOne
    @MapsId("studentId")
    @JoinColumn(name = "student_id")
    private Student student;

    @ManyToOne
    @MapsId("courseId")
    @JoinColumn(name = "course_id")
    private Course course;

    int rating;
}
  • @EmbeddedId 用于标记主键

  • @MapsIdstudentcourseid 映射到主键(在这里是组合键)中去。

    我们之所以要用 @MapsId 是因为,刚刚提到的,组合键的实例域中不能出现实体类型。

接下来配置 studentcourse

@Entity
@Getter
@Setter
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "course")
    private Set<CourseRating> courseRating;
}
@Entity
@Getter
@Setter
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "student")
    private Set<CourseRating> courseRatings;
}

配置完成。

最后更新于