Hibernate 关系映射案例(一对一关系,一对多关系,多对多关系)
本案例使用的hibernate版本为 hibernate-release-5.2.10.Final
为了测试,本案例在mysql数据库中创建了一个db数据库。本案例为了能够将三种关系表达清楚,特地模拟了一个一学生的教务管理系统的一部分,学生登录教务系统需要账号,密码信息,所以在本人把账号,密码信息保存在一张account(账号)表里;在教务系统还存储学生学生的所有的个人信息如:姓名,出生年月,头像,个人简介等信息,这些信息我单独存放在student(学生)表中;教务系统还存储班级信息,我使用了一张数据表classes(班级)表来存储班级名称信息;教务系统还应该有课程,课程的名称,开课时间,结课时间等信息存储在了course(课程)表中。
现在我们看看各个表之间的关系:一个学生只对应一个账号信息,一个账号也只能对应一个学生,所以学生表和账号表属于一对一关系;一个学生只能属于一个班级,但一个班级可以有多名学生,所有班级和学生属于一对多关系;一个学生可以选择多门课程,一门课程可以有多名学生选择,所以学生和课程属于多对多关系;
我们把数据表对应成一个类,下面是类之间的关系图:
下面是简单的数据表的关系图(每张表设置了h_为前缀):
注:多对多关系需要借助第三张表来完成。
创建一个java项目:项目的结构如下:(lib文件夹用于存放jar包)
lib目录下则是hibernate5需要的jar包(注需要将jar加载到项目中:选中jar包右键 --》BuildPath--》Add to Build Path)
log4j.properties为日志配置文件(需要加载配置junit),此项目可以不需要日志,使用日志只是为了观察方便。
注:junit配置可以参考 http://blog.csdn.net/benxiaohai888/article/details/78231911
由于在主配置文件配置了字段创建数据表的配置,所以第一次加载时就会自动创建数据表
配置的代码:
接下来是示例代码:
1、主配置文件 hibernate.cfg.xml
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/db?characterEncoding=utf8
root
org.hibernate.dialect.MySQL5Dialect
true
true
update
thread
4
2、工具类(获取Session)HibernateUtil.java
package cn.sz.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
public static Session getSession() {
Session session = null;
// 创建一个读取主配置文件的对象
Configuration cfg = new Configuration();
// 读取主配置文件
// cfg.configure("hibernate.cfg.xml");如果读取的主配置文件时默认的名字则可以省略参数
cfg.configure();
// 创建SessionFactory
SessionFactory factory = cfg.buildSessionFactory();
// 打开session
// 我们在主配置中已经将session绑定到线程中,所以可以从线程中取出session
// 不从线程中取出session,则可以使用 factory.openSession(); 方式获得session
session = factory.getCurrentSession();
return session;
}
}
3、日志文件 log4j.properties
log4j.rootLogger=DEBUG,console,FILE
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.threshold=INFO
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.maxBackupIndex=100
##log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.FILE.Append=true
log4j.appender.FILE.File=c:/error1.log
log4j.appender.FILE.Threshold=INFO
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.FILE.MaxFileSize=1MB
4、实体类Account.java
package cn.sz.entity;
import java.io.Serializable;
public class Account implements Serializable {
// 学生账号编号id(主属性)
private Integer aid;
// 学生登录账号名
private String aname;
// 密码
private String password;
// 对应另一个一的一方(Student),一个学生对应一个账号,一个账号只对应一个学生(学生和账号属于一对一关系)
private Student student;
public Account() {
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public Integer getAid() {
return aid;
}
public void setAid(Integer aid) {
this.aid = aid;
}
public String getAname() {
return aname;
}
public void setAname(String aname) {
this.aname = aname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
5、实体类Account映射文件 Account.hbm.xml
6、实体类Classes.java
package cn.sz.entity;
import java.io.Serializable;
import java.util.Set;
public class Classes implements Serializable {
// 班级编号id(主属性)
private Integer classid;
// 班级名称
private String classname;
// 对应学生一方,一个班级可以有多个学生,但一个学生只能属于一个班级(班级和学生属于一对多关系)
private Set students;
public Classes() {
}
public Set getStudents() {
return students;
}
public void setStudents(Set students) {
this.students = students;
}
public Integer getClassid() {
return classid;
}
public void setClassid(Integer classid) {
this.classid = classid;
}
public String getClassname() {
return classname;
}
public void setClassname(String classname) {
this.classname = classname;
}
}
7、实体类Classes映射文件Classes.hbm.xml
8、实体类Course.java
package cn.sz.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
public class Course implements Serializable {
// 课程编号id(主属性)
private Integer cid;
// 课程名称
private String cname;
// 开课日期
private Date starttime;
// 结课日期
private Date endtime;
// 对应学生一方,一门课程可以有多个学生选择,一个学生也可以学生多门课程(学生和课程属于多对多关系)
private Set students;
public Course() {
}
public Set getStudents() {
return students;
}
public void setStudents(Set students) {
this.students = students;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public Date getStarttime() {
return starttime;
}
public void setStarttime(Date starttime) {
this.starttime = starttime;
}
public Date getEndtime() {
return endtime;
}
public void setEndtime(Date endtime) {
this.endtime = endtime;
}
}
8、实体类Course映射文件 Course.hbm.xml
9、实体类Student.java
package cn.sz.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
public class Student implements Serializable {
// 学生编号id(主属性)
private Integer stuid;
// 学生姓名
private String stuname;
// 生日
private Date birthday;
// 头像(这里为了演示映射配置文件可以使用byte[] 类型存储)
private byte[] photo;
// 个人说明
private String content;
// 对应一的一方(所属班级),一个班级可以有多个学生,但一个学生只能属于一个班级(班级和学生属于一对多关系)
private Classes classes;
// 对应另一个一的一方(Account),一个学生对应一个账号,一个账号只对应一个学生(学生和账号属于一对一关系)
private Account account;
// 对应课程,一方一门课程可以有多个学生选择,一个学生也可以学生多门课程(学生和课程属于多对多关系)
private Set courses;
public Student() {
}
public Classes getClasses() {
return classes;
}
public void setClasses(Classes classes) {
this.classes = classes;
}
public Set getCourses() {
return courses;
}
public void setCourses(Set courses) {
this.courses = courses;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public Integer getStuid() {
return stuid;
}
public void setStuid(Integer stuid) {
this.stuid = stuid;
}
public String getStuname() {
return stuname;
}
public void setStuname(String stuname) {
this.stuname = stuname;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public byte[] getPhoto() {
return photo;
}
public void setPhoto(byte[] photo) {
this.photo = photo;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
10、实体类Student映射文件 Student.hbm.xml
测试类:
11、TestStudentOrAccount.java (测试Student和Account 一对一关系)
package cn.sz.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import org.hibernate.Session;
import org.junit.Test;
import cn.sz.entity.Account;
import cn.sz.entity.Student;
import cn.sz.utils.HibernateUtil;
public class TestStudentOrAccount {
@Test
// 演示Student和Account一对一关系添加方法
public void testSave() throws IOException {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 创建一个账号
Account account = new Account();
account.setAname("zs123");
account.setPassword("123");
// 创建一个学生(张三)
Student stu1 = new Student();
stu1.setStuname("张三");
stu1.setBirthday(new Date());
stu1.setContent("张三的个人描述。。。");
// 获取桌面的一张演示图片,将图片转换为字节数组存储到数据库(利用字节输入流读取并将字节数组保存到数据库)
InputStream is = new FileInputStream(new File("C:\\Users\\Administrator\\Desktop\\test.jpg"));
byte[] photo = new byte[is.available()];
is.read(photo);
stu1.setPhoto(photo);
// 将账号与学生建立联系,此账号属于只属于这个学生
account.setStudent(stu1);
// 保存到数据库中
session.save(account);
session.save(stu1);
session.getTransaction().commit();
is.close();
session.close();
}
@Test
// 演示Student和Account一对一关系删除方法
public void testDelete() {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 获取学生编号为1学生
Student student = session.get(Student.class, 1);
// 删除该学生(发现student表(主表)学生的该学生的信息被删除,account表(从表)的对应概述的信息也被删除,因为我们在映射文件中配置当主表数据删除时从表和它关联的数据也跟着删除(cascade="delete")
session.delete(student);
// 发现当我们删除从表的数据时主表的对应的学生信息并不会删除
// Account account = session.get(Account.class, 1);
// session.delete(account);
session.getTransaction().commit();
session.close();
}
@Test
// 演示Student和Account一对一关系查询方法
public void testQuery() throws IOException {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 根据学生编号获取学生信息和学生登录账号
Student student = session.get(Student.class, 1);
System.out.println("学生姓名: " + student.getStuname());
// 将图片字节写出到桌面
byte[] photo = student.getPhoto();
OutputStream os = new FileOutputStream(new File("C:\\Users\\Administrator\\Desktop\\test1.jpg"));
os.write(photo);
Account account = student.getAccount();
System.out.println("学生登录账号:" + account.getAname());
System.out.println("------------------------");
// 根据账号表的账号编号获取学生信息学生登录账号
Account account2 = session.get(Account.class, 1);
Student student2 = account2.getStudent();
System.out.println("学生姓名: " + student2.getStuname());
System.out.println("学生登录账号:" + account2.getAname());
session.getTransaction().commit();
os.close();
session.close();
}
}
12、TestStudentOrClasses.java (测试Classes和Student 一对多关系)
package cn.sz.test;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.junit.Test;
import cn.sz.entity.Classes;
import cn.sz.entity.Student;
import cn.sz.utils.HibernateUtil;
public class TestStudentOrClasses {
@Test
// 演示Classes和Student一对多添加方法
public void testSave() {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 创建一个班级 A班
Classes classes = new Classes();
classes.setClassname("A班");
// 创建一个学生(小白)
Student student = new Student();
student.setStuname("小白");
student.setClasses(classes);
// 创建一个学生(小花)
Student student2 = new Student();
student2.setStuname("小花");
student2.setClasses(classes);
// 两个同学都在A班
HashSet set = new HashSet<>();
set.add(student);
set.add(student2);
// classes.setStudents(set);
// 保存到数据库
session.save(student);
session.save(student2);
session.save(classes);
session.getTransaction().commit();
session.close();
}
@Test
// 演示Classes和Student一对多删除方法
public void testDelete() {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 删除班级编号为1
// 的班级,将发现该班级下面的学生都删除了,因为我们在映射文件中配置当主表数据删除时从表和它关联的数据也跟着删除(cascade="delete")
Classes classes = session.get(Classes.class, 4);
session.delete(classes);
session.getTransaction().commit();
session.close();
}
@Test
// 演示Classes和Student一对多查询方法
public void testQuery() {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 查询班级编号为1的班级信息和有哪些学生
Classes classes = session.get(Classes.class, 4);
System.out.println("班级名称:" + classes.getClassname());
Set students = classes.getStudents();
System.out.println("班级的学生:");
for (Student student : students) {
System.out.println(student.getStuname());
}
session.getTransaction().commit();
session.close();
}
}
13、TestStudentOrCourse.java (测试Student和Course 多对多关系)
package cn.sz.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.junit.Test;
import cn.sz.entity.Account;
import cn.sz.entity.Course;
import cn.sz.entity.Student;
import cn.sz.utils.HibernateUtil;
public class TestStudentOrCourse {
@Test
// 演示Student和Course多对多关系添加方法
public void testSave() throws IOException {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 创建一个学生(李四)
Student stu1 = new Student();
stu1.setStuname("李四");
stu1.setBirthday(new Date());
stu1.setContent("李四的个人描述。。。");
// 创建一个学生(王五)
Student stu2 = new Student();
stu2.setStuname("王五");
stu2.setBirthday(new Date());
stu2.setContent("王五的个人描述。。。");
// 创建一个学生(赵六)
Student stu3 = new Student();
stu3.setStuname("赵六");
stu3.setBirthday(new Date());
stu3.setContent("王五的个人描述。。。");
// 创建一门课程(java开发技术)
Course course1 = new Course();
course1.setCname("java开发技术");
course1.setStarttime(new Date());
course1.setEndtime(new Date());
// 创建一门课程(php开发技术)
Course course2 = new Course();
course2.setCname("php开发技术");
course2.setStarttime(new Date());
course2.setEndtime(new Date());
// 创建一门课程(c++开发技术)
Course course3 = new Course();
course3.setCname("c++开发技术");
course3.setStarttime(new Date());
course3.setEndtime(new Date());
// 现在李四选择 java开发技术和php开发技术 两门课程
HashSet set1 = new HashSet<>();
set1.add(course1);
set1.add(course2);
stu1.setCourses(set1);
// 现在王五选择 java开发技术和c++开发技术 两门课程
HashSet set2 = new HashSet<>();
set2.add(course1);
set2.add(course3);
stu2.setCourses(set2);
// 现在赵六选择 java开发技术和php开发技术和c++开发技术 三门课程
HashSet set3 = new HashSet<>();
set3.add(course1);
set3.add(course2);
set3.add(course3);
stu3.setCourses(set3);
// 现在java开发技术有3人选择 ,php开发技术有2人选择, c++开发技术有2人选择
// 保存到数据库中
session.save(stu1);
session.save(stu2);
session.save(stu3);
session.save(course1);
session.save(course2);
session.save(course3);
session.getTransaction().commit();
session.close();
}
@Test
// 演示Student和Course多对多关系删除方法
public void testDelete() {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 获取学生编号为2学生(李四)
Student student = session.get(Student.class, 2);
// 删除该学生(发现student表该学生的信息被删除,h_course_student表该学生选择的课程也将该学生移除
session.delete(student);
// 获取kec编号为3 的课程,删除该课程后,h_course_student表该课程的对应信息也删除了
Course Course = session.get(Course.class, 3);
session.delete(Course);
session.getTransaction().commit();
session.close();
}
@Test
// 演示Student和Course多对多关系查询方法
public void testQuery() throws IOException {
Session session = HibernateUtil.getSession();
session.beginTransaction();
// 根据学生编号获取学生信息和学生现在的课程(就是查看该学生选择了那些课程)
Student student = session.get(Student.class, 4);
System.out.println("学生姓名: " + student.getStuname());
Set courses = student.getCourses();
System.out.println("选择的课程:");
for (Course course : courses) {
System.out.println(course.getCname());
}
System.out.println("------------------------");
// 根据课程表编号课程信息和选择该课程的学生信息(就是查看该课程有哪些学生选择)
Course course = session.get(Course.class, 2);
System.out.println("课程名称: " + course.getCname());
Set students = course.getStudents();
System.out.println("选择该课程的学生:");
for (Student student2 : students) {
System.out.println(student2.getStuname());
}
session.getTransaction().commit();
session.close();
}
}
总体预览:
班级(classes)表结构:
课程表和学生表生成的中间表(course_student)结构:
学生(student)表结构:
账号(account)表结构:
课程(course)表结构:
下面是测试的结果:
接下来我们先测试student(学生) 和 account(账号)一对一关系:
测试的类为:TestStudentOrAccount.java
1、执行方法 testSave() 添加方法
我们发现信息添加到了两张数据表之中,两张表也建立了联系,关联的字段 stuid,账号表的stuid对应学生表的stuid.
2、执行testQuery()查询方法
我们发现能够使用学生表为主来查询到两张表的信息,也能够使用账号表为主来查询到两张表的信息。
3、执行testDelete() 删除方法
我们发现当主表的数据删除从表的数据也跟着删除了(我们配置了casade="delete",当删除主表数据时,从表关联数据也跟着删除)
接下来我们先测试student(学生) 和 course(课程)多对多关系:
测试的类为:TestStudentOrCourse.java
1、执行方法 testSave() 添加方法
发现数据添加到数据表中,学生表和课程在第三张中建立联系,cid对应课程表的cid字段,stuid对应学生表的stuid字段。
能够根据学生查询出他选择的课程,也可以根据课程查看选择该课程的学生。
3、执行testDelete() 删除方法
删除学生表的数据,发现在第三张表的他关联的信息也被删除了,删除课程发现在第三张表他关联的数据也跟着删除了。
接下来我们先测试student(学生) 和 classes(班级)一对多关系:
测试的类为:TestStudentOrClasses.java
1、执行方法 testSave() 添加方法
添加数据,两张表建立了联系,学生表的classid对应班级表的classid
能够根据班级查询他的所有学生。
3、执行testDelete() 删除方法
删除班级后,在这个班级里面的学生也被删除了(在设置关联的时候设置了 cascade="delete",主表数据删除,从表关联的数据也跟着删除)