详谈Hibernate框架关系映射!
接触Hibernate也有一小段的时间了,愈发的觉得Hibernate是个神奇的东西,为什么这么说呢?因为你可以不懂一行sql,直接面向对象,就可以将数据直接保存到数据库去!!
你还可以保存一个对象,然后一次性的将与它相关的所有数据保存到数据库,比如说,你只需要保存班级对象,就可以将该班级信息和该班级下的所有学生在数据库中形成一堆的记录。
而且都不需要你写sql!!!
有木有很神奇。。。。反正宝宝我是惊呆了。
下面就拿具体的代码实现来讲吧~
首先讲一个简单的 单向一对多的案例(以班级和学生作为案例)
众所周知,Hibernate运用的是一种面向对象的思想,我们想要与数据库相关联,首先我们得必须有与之相对应的实体类
比如说,我有一个学生对象和班级对象,分别对应数据库中的学生表和班级表具体信息如下:
package entity; /*
*学生表
*/ import java.io.Serializable; public class Student implements Serializable { private Integer sid;//学生编号 private String sname;//学生姓名 private String sex;//学生性别 public Student() { } public Student(String sname, String sex) { this.sname = sname; this.sex = sex; } public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
package entity; /* *班级表 */ import java.io.Serializable; import java.util.HashSet; import java.util.List; import java.util.Set; public class Grade implements Serializable { private Integer gid;//班级编号 private String gname;//班级名称 private String gdesc;//班级描述 public Grade() { } public Grade(String gname, String gdesc) { this.gname = gname; this.gdesc = gdesc; } public Integer getGid() { return gid; } public void setGid(Integer gid) { this.gid = gid; } public String getGname() { return gname; } public void setGname(String gname) { this.gname = gname; } public String getGdesc() { return gdesc; } public void setGdesc(String gdesc) { this.gdesc = gdesc; } }
一对多的话,应该是比较好理解的,因为我们可以理解为 一个班级可以以对应多个学生,这就是一对多,既然一个班级对应多个学生的话,那么我们是不是就可以在班级的实体类
中加入一个学生集合和呢?这样是不是更能体现出一对多的关系呢?所以我们对班级实体就有了下面的改造
package entity; import java.io.Serializable; import java.util.HashSet; import java.util.List; import java.util.Set; public class Grade implements Serializable { private Integer gid;//年级编号 private String gname;//年级名称 private String gdesc;//年级描述 //添加一个班级里的学生集合 private Set<Student> stus=new HashSet<Student>(); public Set<Student> getStus() { return stus; } public void setStus(Set<Student> stus) { this.stus = stus; } public Grade() { } public Grade(String gname, String gdesc) { this.gname = gname; this.gdesc = gdesc; } public Integer getGid() { return gid; } public void setGid(Integer gid) { this.gid = gid; } public String getGname() { return gname; } public void setGname(String gname) { this.gname = gname; } public String getGdesc() { return gdesc; } public void setGdesc(String gdesc) { this.gdesc = gdesc; } }
实体类写完了,我们就该写最关键的配置文件也就是映射文件了(Grade.hbm.xml)
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 对应所在的包 --> <hibernate-mapping package="entity"> <!-- 实体类和数据表的名称 --> <class name="Grade" table="Grade"> <!-- 实体类中和数据表中所对应的主键 --> <id name="gid" column="gid"> <!-- 主键生成策略 increment是值找到最大的主键 值,并加1 --> <generator class="increment" /> </id> <!-- 非主键属性的配置 --> <property name="gname" column="gname" /> <property name="gdesc" column="gdesc"/> <!-- 配置多对一配置信息 --> <set name="stus" table="Student" > <!-- 多方的外建值 --> <key column="gid"></key> <one-to-many class="entity.Student"/> </set> </class> </hibernate-mapping>
这样我们就完成了一对多的配置了,此时,我们不用对Student.hbm.xml做任何操作,下面可以测试了
//单向一对多案例(一个班级对应多个学生) public static void DOneToManyAdd(){ //准备session Session session=HibernateUtil.currentSession(); //开启事务 Transaction tx = session.beginTransaction(); //创建一个班级 Grade grade=new Grade("S1261","无敌的Y1261班"); //准备几个学生 Student stu1=new Student("微热的雪","女"); Student stu2=new Student("巴黎的雨季","男"); //设置班级里的学生 grade.getStus().add(stu1); grade.getStus().add(stu2); //保存 session.save(grade); session.save(stu1); session.save(stu2); //提交事务 tx.commit(); //关闭连接 HibernateUtil.closeSession(); }
执行这些代码后可以在控制台看到如下信息
这个时候,你的数据库中便有了如下信息
可以从上面的测试代码中看出,我并没有手动的指定学生所在的班级,但是因为有映射文件,Hibernate会自动的检索到所在的班级并自行的发送sql语句到数据库进行持久化操作。
这就是Hibernate的强大之处,当然,这只是一个最简单的例子,下面就跟着我看看更加有趣的例子吧!
Hibernate关系映射二之 单向多对一关系映射
多对一关系映射也同样的好理解,比如,多个学生可以同时处于一个班级下,这就是单向的多对一的关系,所以我们就可以想到在学生表中加入一个班级属性
package entity; import java.io.Serializable; public class Student implements Serializable { private Integer sid;//学生编号 private String sname;//学生姓名 private String sex;//学生性别 //创建一个班级 private Grade grade; public Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } public Student() { } public Student(String sname, String sex) { this.sname = sname; this.sex = sex; } public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
因为是单向的多对一,所以我们只需要在多的一方,也就是学生方的配置文件中进行修改,班级方的配置文件保持原始(也就是没有set标签的时候)
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="entity"> <class name="Student" table="Student"> <id name="sid" column="sid"> <generator class="increment" /> </id> <property name="sname" column="sname" /> <property name="sex" column="sex"/> <!-- 配置多对一的关系映射 --> <many-to-one name="grade" class="entity.Grade" column="gid"></many-to-one> </class> </hibernate-mapping>
同样,我们做一个单向多对一的添加操作
//单向多对一添加案例(多个学生对应一个班级) public static void DManyToOneAdd(){ //准备session Session session=HibernateUtil.currentSession(); //开启事务 Transaction tx = session.beginTransaction(); //创建一个班级 Grade grade=new Grade("S2222班","挺6的S2222班"); //准备几个学生 Student stu1=new Student("恩恩","男"); Student stu2=new Student("呵呵","女"); //设置学生所在的班级 stu1.setGrade(grade); stu2.setGrade(grade); //保存 session.save(grade); session.save(stu1); session.save(stu2); //提交事务 tx.commit(); //关闭连接 HibernateUtil.closeSession(); }
注意!!!此时的Hibernate生成的sql语句与一对多时是不一样的!
数据库中同样也是有相对应的记录
经过上面两个案例的展示,可能有同学就会有疑问了,既然多个学生可以属于一个班级,一个班级又可以有多个学生,那么他们俩之间到底可以设为 什么关系呢?
此时,我们就可以设置为 双向的一对多的关系了。因为班级和学生是一个双向的关系,而且一个班级又有多个学生
这时我们完整的配置文件就是以上的两个总和了
Student.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="entity"> <class name="Student" table="Student"> <id name="sid" column="sid"> <generator class="increment" /> </id> <property name="sname" column="sname" /> <property name="sex" column="sex"/> <!-- 配置多对一的关系映射 --> <many-to-one name="grade" class="entity.Grade" column="gid" ></many-to-one> </class> </hibernate-mapping>
Grade.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 对应所在的包 --> <hibernate-mapping package="entity"> <!-- 实体类和数据表的名称 --> <class name="Grade" table="Grade"> <!-- 实体类中和数据表中所对应的主键 --> <id name="gid" column="gid"> <!-- 主键生成策略 increment是值找到最大的主键 值,并加1 --> <generator class="increment" /> </id> <!-- 非主键属性的配置 --> <property name="gname" column="gname" /> <property name="gdesc" column="gdesc"/> <!-- 配置多对一配置信息 --> <set name="stus" table="Student" > <!-- 多方的外建值 --> <key column="gid"></key> <one-to-many class="entity.Student"/> </set> </class> </hibernate-mapping>
测试数据
//双向添加案例 private static void SAdd(){ //准备session Session session=HibernateUtil.currentSession(); //开启事务 Transaction tx = session.beginTransaction(); //创建一个班级 Grade grade=new Grade("S2222班","挺6的S2222班"); //准备几个学生 Student stu1=new Student("巴黎的雨季","男"); Student stu2=new Student("微热的雪","女"); //设置班级下的学生 grade.getStus().add(stu1); grade.getStus().add(stu2); //为学生设置班级 stu1.setGrade(grade); stu2.setGrade(grade); //保存 session.save(grade); session.save(stu1); session.save(stu2); //提交事务 tx.commit(); //关闭连接 HibernateUtil.closeSession(); }
细心的同学会发现,当我执行了上面的代码时,效果与我设置多对一和一对多的效果一样,而且这还比较的繁琐和复杂,所以这并不是双向关系的优势
这里我们就要用到cascade(级联)的属性了 设置级联的属性后,因为有 双向的关系,所以当你只添加班级的时候Hibernate会自动的添加班级下的学生
Student.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="entity"> <class name="Student" table="Student"> <id name="sid" column="sid"> <generator class="increment" /> </id> <property name="sname" column="sname" /> <property name="sex" column="sex"/> <!-- 配置多对一的关系映射 --> <many-to-one name="grade" class="entity.Grade" column="gid" cascade="all"></many-to-one> </class> </hibernate-mapping>
Grade.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 对应所在的包 --> <hibernate-mapping package="entity"> <!-- 实体类和数据表的名称 --> <class name="Grade" table="Grade"> <!-- 实体类中和数据表中所对应的主键 --> <id name="gid" column="gid"> <!-- 主键生成策略 increment是值找到最大的主键 值,并加1 --> <generator class="increment" /> </id> <!-- 非主键属性的配置 --> <property name="gname" column="gname" /> <property name="gdesc" column="gdesc"/> <!-- 配置多对一配置信息 --> <set name="stus" table="Student" cascade="all" inverse="true"> <!-- 多方的外建值 --> <key column="gid"></key> <one-to-many class="entity.Student"/> </set> </class> </hibernate-mapping>
这样当我们设置级联的属性后,测试代码如下
//双向添加案例(添加班级自动添加班级下的学生) private static void SAdd(){ //准备session Session session=HibernateUtil.currentSession(); //开启事务 Transaction tx = session.beginTransaction(); //创建一个班级 Grade grade=new Grade("S2222班","挺6的S2222班"); //准备几个学生 Student stu1=new Student("巴黎的雨季","男"); Student stu2=new Student("微热的雪","女"); //设置班级下的学生 grade.getStus().add(stu1); grade.getStus().add(stu2); //为学生设置班级 stu1.setGrade(grade); stu2.setGrade(grade); //保存(设置级联属性,自动关联该班级下的学生) session.save(grade); //提交事务 tx.commit(); //关闭连接 HibernateUtil.closeSession(); }
这样,我们只用写save(grade) 保存班级,这时Hibernate会生成如下代码
至此,基本上就讲完了Hibernate中单向关系映射的知识点了,明天为大家讲解关于双向多对多关系映射的知识点。