《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现

这个马士兵老师的Hibernate视频学习的一个题目,这里面要用到多对多、多对一的关联关系以及联合主键,因此觉得挺好的,自己写篇博文来记录下。

先考虑数据库表

1、学生表:为简单起见,只考虑了学生id和学生姓名,其中id为主键

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现_第1张图片

2、课程表:为简单起见,只考虑了课程id和课程名称,其中id为主键

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现_第2张图片

3、分数表

分数表有两种解决方案

3.1 第一种为:使用联合主键:student_id 和 course_id

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现_第3张图片

3.2 第二种:不使用联合主键,而使用id作为主键

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现_第4张图片

在这里,这篇博文所采取的方法是采用第二种方法:即不使用联合主键,使用id作为主键

在考虑实体类,以及它们的映射关系

一个学生可以选择多个课程,一个课程可以被多个学生选择,即学生和课程是一个多对多的关系。而一个学生可以有多个分数,因此,分数与学生是多对一的关系,同理,分数与课程也是多对一的关系。

下面开始设计

Student实体与Course类之间时多对多的关系,但是,一般情况下,我们需要通过学生来获取他的全部课程,因此,我们需要这样一个导向,有时,我们也需要通过课程,来提取选择这门课的学生,因此,我们建立双向关联。

Student类

@Entity
    public class Student {

    private int id;
    private String name;
    /* * Student和Course是多对多的关系, * 由于我们一般需要通过Student来获取Course,而不需要通过Course来获取Student, * 因此我们建立一个单向导向 * */
    private Set<Course> courses=new HashSet<Course>();
    @ManyToMany(cascade=CascadeType.ALL)
    //设置中间表,中间表必须的名称必须与Score一致
    @JoinTable(name = "score",
            joinColumns = @JoinColumn(name="student_id"),
            inverseJoinColumns = @JoinColumn(name="course_id")
        )
    public Set<Course> getCourses() {
        return courses;
    }
    public void setCourses(Set<Course> courses) {
        this.courses = courses;
    }
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

Course类

@Entity
public class Course {

    private int id;
    private String name;
    private Set<Student> students=new HashSet<Student>();
    @ManyToMany(mappedBy="courses")
    public Set<Student> getStudents() {
        return students;
    }
    public void setStudents(Set<Student> students) {
        this.students = students;
    }
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

Score类

Score与Student、Course都是多对一的关联关系,因此,我们需要建立ManyToOne的关联关系。

@Entity
@Table(name="score")
public class Score {

    private int id;
    /* * Score与Student、Course都是多对一的关联关系, * 且Student、Course是多对多的关联关系 * */
    private Student student;
    private Course course;
    private int score;

    @Id
    @GeneratedValue
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @ManyToOne
    @JoinColumn(name="student_id")
    public Student getStudent() {
        return student;
    }
    public void setStudent(Student student) {
        this.student = student;
    }
    @ManyToOne
    @JoinColumn(name="course_id")
    public Course getCourse() {
        return course;
    }
    public void setCourse(Course course) {
        this.course = course;
    }

    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }


}

这样我们就完成了实体类的设计。

测试

通过如下的代码可以观察建表语句。

public  void testSchema(){
        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build();
        Metadata metadata = new MetadataSources(serviceRegistry).buildMetadata();
        SchemaExport schemaExport = new SchemaExport();
        schemaExport.create(EnumSet.of(TargetType.DATABASE), metadata);


    }

建表语句如下:

从建表语句可以看出,生成的Score表是采用的是student_id和course_id的联合主键,而在Score实体类中,我们是采用的@Id来修饰的id,Hibernate并没有按照@id来进行生成表,而是根据Student类和Course实体类的中间表score来进行生成的。

由于生成的表与我们的目的我不符合,因此,我们可以选择手动建表,建表语句如下:

测试Save方法,即持久化对象

有一个学生,学了两门课,有两个成绩

@Test
public void testSave(){
        /* * 有一个学生,学了一门课,有一个成绩 * */
        Student s1=new Student();
        s1.setName("wu");


        Course c1=new Course();
        c1.setName("math");
        Course c2=new Course();
        c2.setName("english");
        /* * 困惑:加上如下的两条语句将会发出两条insert into score values(null,null); * */
    //      s1.getCourses().add(c1);
    //      s1.getCourses().add(c2);

        Score sc1=new Score();
        sc1.setScore(98);
        sc1.setStudent(s1);
        sc1.setCourse(c1);

        Score sc2=new Score();
        sc2.setScore(77);
        sc2.setStudent(s1);
        sc2.setCourse(c2);

        //开启事务进行持久化操作
        Session s=sessionFactory.getCurrentSession();
        s.beginTransaction();
        s.save(s1); 
        s.save(c1); 
        s.save(c2); 
        s.save(sc1);
        s.save(sc2); 
        s.getTransaction().commit();

    }

正确的持久化的结果在数据库中结果如下:

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现_第5张图片

在测试中,当我们在测试代码中,加上如下的两行代码(这两行代码在上面的代码块中已注释掉了),

s1.getCourses().add(c1);
s1.getCourses().add(c2);//这两行代码的意图就是将课程加入到学生中

就会得到如下的结果:即为我们自动生成了两行成绩为NULL的行数据。

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现_第6张图片

遇到的问题:Field ‘id’ doesn’t have a default value

在完成这个测试的过程中,遇到了如下的问题:

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现_第7张图片

原因是我们手动建表时,没有为主键id设置auto_increment.

你可能感兴趣的:(Hibernate,数据库,解决方案,多对一,多对多)