转载自 https://blog.csdn.net/u010412719/article/details/51353806
Hibernate 学生、课程、分数关系的设计与实现
这个马士兵老师的Hibernate视频学习的一个题目,这里面要用到多对多、多对一的关联关系以及联合主键,因此觉得挺好的,自己写篇博文来记录下。
先考虑数据库表
1、学生表:为简单起见,只考虑了学生id和学生姓名,其中id为主键
2、课程表:为简单起见,只考虑了课程id和课程名称,其中id为主键
3、分数表
分数表有两种解决方案
3.1 第一种为:使用联合主键:student_id 和 course_id
3.2 第二种:不使用联合主键,而使用id作为主键
在这里,这篇博文所采取的方法是采用第二种方法:即不使用联合主键,使用id作为主键
在考虑实体类,以及它们的映射关系
一个学生可以选择多个课程,一个课程可以被多个学生选择,即学生和课程是一个多对多的关系。而一个学生可以有多个分数,因此,分数与学生是多对一的关系,同理,分数与课程也是多对一的关系。
下面开始设计
Student实体与Course类之间时多对多的关系,但是,一般情况下,我们需要通过学生来获取他的全部课程,因此,我们需要这样一个导向,有时,我们也需要通过课程,来提取选择这门课的学生,因此,我们建立双向关联。
Student类
1 @Entity 2 public class Student { 3 4 private int id; 5 private String name; 6 /* 7 * Student和Course是多对多的关系, 8 * 由于我们一般需要通过Student来获取Course,而不需要通过Course来获取Student, 9 * 因此我们建立一个单向导向 10 * */ 11 private Set<Course> courses=new HashSet<Course>(); 12 @ManyToMany(cascade=CascadeType.ALL) 13 //设置中间表,中间表必须的名称必须与Score一致 14 @JoinTable(name = "score", 15 joinColumns = @JoinColumn(name="student_id"), 16 inverseJoinColumns = @JoinColumn(name="course_id") 17 ) 18 public Set<Course> getCourses() { 19 return courses; 20 } 21 public void setCourses(Set<Course> courses) { 22 this.courses = courses; 23 } 24 @Id 25 @GeneratedValue(strategy = GenerationType.AUTO) 26 public int getId() { 27 return id; 28 } 29 public void setId(int id) { 30 this.id = id; 31 } 32 public String getName() { 33 return name; 34 } 35 public void setName(String name) { 36 this.name = name; 37 } 38 39 }
Course类
1 @Entity 2 public class Course { 3 4 private int id; 5 private String name; 6 private Set<Student> students=new HashSet<Student>(); 7 @ManyToMany(mappedBy="courses") 8 public Set<Student> getStudents() { 9 return students; 10 } 11 public void setStudents(Set<Student> students) { 12 this.students = students; 13 } 14 @Id 15 @GeneratedValue(strategy = GenerationType.AUTO) 16 public int getId() { 17 return id; 18 } 19 public void setId(int id) { 20 this.id = id; 21 } 22 public String getName() { 23 return name; 24 } 25 public void setName(String name) { 26 this.name = name; 27 } 28 29 }
Score类
Score与Student、Course都是多对一的关联关系,因此,我们需要建立ManyToOne的关联关系。
1 @Entity 2 @Table(name="score") 3 public class Score { 4 5 private int id; 6 /* 一个课程可以有多个成绩(88,98,。。) 一个学生可以有多个分数(数学,物理,。。) 7 * Score与Student、Course都是多对一的关联关系, 8 * 且Student、Course是多对多的关联关系 9 * */ 10 private Student student; 11 private Course course; 12 private int score; 13 14 @Id 15 @GeneratedValue 16 public int getId() { 17 return id; 18 } 19 public void setId(int id) { 20 this.id = id; 21 } 22 @ManyToOne 23 @JoinColumn(name="student_id") 24 public Student getStudent() { 25 return student; 26 } 27 public void setStudent(Student student) { 28 this.student = student; 29 } 30 @ManyToOne 31 @JoinColumn(name="course_id") 32 public Course getCourse() { 33 return course; 34 } 35 public void setCourse(Course course) { 36 this.course = course; 37 } 38 39 public int getScore() { 40 return score; 41 } 42 public void setScore(int score) { 43 this.score = score; 44 } 45 46 47 }
测试
通过如下的代码可以观察建表语句。
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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
建表语句如下:
从建表语句可以看出,生成的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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
正确的持久化的结果在数据库中结果如下:
在测试中,当我们在测试代码中,加上如下的两行代码(这两行代码在上面的代码块中已注释掉了),
s1.getCourses().add(c1);
s1.getCourses().add(c2);//这两行代码的意图就是将课程加入到学生中
- 1
- 2
就会得到如下的结果:即为我们自动生成了两行成绩为NULL的行数据。
遇到的问题:Field ‘id’ doesn’t have a default value
在完成这个测试的过程中,遇到了如下的问题:
原因是我们手动建表时,没有为主键id设置auto_increment.