一、Hibernate的执行流程
hibernate作为一个ORM框架,它封装了大量数据库底层的sql语句操作的方法,这样在执行hibernate的过程中理解hibernate的执行流程很有必要。
由上图我们可以很清楚的发现,想要获得一个sessionFactory对象,需要进行很多步骤,需要读取和加载文件信息,内存消耗很大,所以一般一个数据库只会生成一个sessionFactory对象。
代码展示hibernate:
1 import java.util.Date; 2 import java.util.List; 3 5 import org.hibernate.Session; 6 import org.hibernate.SessionFactory; 7 import org.hibernate.Transaction; 8 import org.hibernate.cfg.Configuration; 9 import org.hibernate.service.ServiceRegistry; 10 import org.hibernate.service.ServiceRegistryBuilder; 11 import org.junit.After; 12 import org.junit.Before; 13 import org.junit.Test; 14 15 import Entity.Students; 16 17 //测试类 18 public class StudentsTest { 19 private SessionFactory sessionFactory; 20 private Session session; 21 private Transaction transaction; 22 @Before 23 public void init() 24 { 25 //匹配hibernate.cfg.xml文件获得配置对象 26 Configuration config=new Configuration().configure(); 27 //获得服务注册对象(该对象中封装了hibernate.cfg.xml文档的<properties>、<mapping>标签信息) 28 ServiceRegistry serviceRegistry=new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry(); 29 //获得sessionFactory对象(将ServiceRegistry中的相关信息,封装到sessionFactory中) 30 sessionFactory=config.buildSessionFactory(serviceRegistry); 31 //获得session对象 32 session=sessionFactory.openSession(); 33 //开启事务 34 transaction=session.beginTransaction(); 35 36 } 37 38 39 @Test 40 public void testSaveStudents() 41 { 42 Students stu=new Students(5,"小宏","女",new Date(),"华山"); 43 session.save(stu); 44 } 45 46 @After 47 public void Destory() 48 { 49 //提交事务 50 transaction.commit(); 51 //关闭会话 52 session.close(); 53 //关闭会话工厂 54 sessionFactory.close(); 55 } 56 57 }
二、hibernate中映射问题--以配置文件方式的介绍(特别是外键约束)
1、一对多的关系映射
代码示例:
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.password">tiger</property> <property name="hibernate.connection.url">jdbc:mysql:///hibernate?useUnicode=true&characterEncoding=UTF-8</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="hbm2ddl.auto">create</property> <mapping resource="com/third/Dao1/Grader1.hbm.xml"/> <mapping resource="com/third/Dao1/Students1.hbm.xml"/> </session-factory> </hibernate-configuration>
Students1.java
package com.third.Dao1; import java.io.Serializable; public class Students1 implements Serializable { private int sid; private String sname; private String sgender; public Students1() { } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSgender() { return sgender; } public void setSgender(String sgender) { this.sgender = sgender; } }
Grader1.java
package com.third.Dao1; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Grader1 implements Serializable { private int gid; private String gname; private Set<Students1> stuSet=new HashSet<Students1>(); public Grader1() { } public int getGid() { return gid; } public void setGid(int gid) { this.gid = gid; } public String getGname() { return gname; } public void setGname(String gname) { this.gname = gname; } public Set<Students1> getStuSet() { return stuSet; } public void setStuSet(Set<Students1> stuSet) { this.stuSet = stuSet; } }
由eclipse帮助生成的:
Students1.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-1 20:42:49 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao1.Students1" table="STUDENTS1"> <id name="sid" type="int"> <column name="SID" /> <generator class="native" /> </id> <property name="sname" type="java.lang.String"> <column name="SNAME" /> </property> <property name="sgender" type="java.lang.String"> <column name="SGENDER" /> </property> </class> </hibernate-mapping>
Grager1.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-1 20:42:49 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao1.Grader1" table="GRADER1"> <id name="gid" type="int"> <column name="GID" /> <generator class="increment" /> </id> <property name="gname" type="java.lang.String"> <column name="GNAME" /> </property> <set name="stuSet" inverse="false" table="STUDENTS1" lazy="true"> <key> <column name="GID" /> </key> <one-to-many class="com.third.Dao1.Students1" /> </set> </class> </hibernate-mapping>
Test1.java
package com.third; import java.util.HashSet; import java.util.Set; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.third.Dao1.Grader1; import com.third.Dao1.Students1; /*import com.third.Dao.Grader; import com.third.Dao.Students;*/ public class Test1 { private static SessionFactory sessionFactory; private static Session session; private static Transaction transaction; @Before public void init(){ //创建配置对象,匹配读取hibernate.cfg.xml文件 Configuration config=new Configuration().configure(); //获取服务注册对象(该对象中封装了hibernate.cfg.xml文件中的<properties>、<mapping>信息) ServiceRegistry serviceRegistry=new ServiceRegistryBuilder() .applySettings(config.getProperties()).buildServiceRegistry(); //获取sessionFactory对象(该对象中通过传参serviceRegistry对象,获取了<mapping><properties>信息) sessionFactory=config.buildSessionFactory(serviceRegistry); } @Test public void test2(){ //通过sessionFactory对象获取session对象 session=sessionFactory.openSession(); //通过session对象开启事务,并返回Transaction对象 transaction=session.beginTransaction(); //创建students和Grader对象 Students1 student2=new Students1(); student2.setSname("小美"); student2.setSgender("女"); Students1 student3=new Students1(); student3.setSname("小宏"); student3.setSgender("女"); Set<Students1> stuSet=new HashSet<Students1>(); stuSet.add(student2); stuSet.add(student3); //实例Grader对象 Grader1 grader2=new Grader1(); grader2.setGname("信息与计算科学二班"); grader2.setStuSet(stuSet);; session.save(student2); session.save(student3); session.save(grader2); } @After public void destory(){ //提交事务 transaction.commit(); //关闭资源 if(session!=null){ session.close(); } if(sessionFactory!=null){ sessionFactory.close(); } } }
运行结果数据库表格记录和表格样式截图:
表格样式:
表格记录:
我们不难看出,对于一对多的映射,其核心的代码是背景为绿色的代码段,即在一方的bean中添加多方的引用的集合,并且在其一方的bean的映射文件hbm.xml中添加<one-to-many>标签进行配置设置。hbm.xml文件中指出了多方的主键作为一方的一个外键,我们通过分析表格样式,可以发现eclipse为一方生成了一个与多方主键关联的外键。
2、多对一映射
代码示例:
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.password">tiger</property> <property name="hibernate.connection.url">jdbc:mysql:///hibernate?useUnicode=true&characterEncoding=UTF-8</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="hbm2ddl.auto">create</property> <mapping resource="com/third/Dao/Grader.hbm.xml"/> <mapping resource="com/third/Dao/Students.hbm.xml"/> </session-factory> </hibernate-configuration>
Grader.java
package com.third.Dao; import java.io.Serializable; public class Grader implements Serializable { private int gid; private String gname; public Grader() { } public int getGid() { return gid; } public void setGid(int gid) { this.gid = gid; } public String getGname() { return gname; } public void setGname(String gname) { this.gname = gname; } }
Students.java
package com.third.Dao; import java.io.Serializable; public class Students implements Serializable { private int sid; private String sname; private String sgender; private Grader grader; public Students() { } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSgender() { return sgender; } public void setSgender(String sgender) { this.sgender = sgender; } public Grader getGrader() { return grader; } public void setGrader(Grader grader) { this.grader = grader; } }
Grader.cfg.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-1 19:15:12 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao.Grader" table="GRADER"> <id name="gid" type="int"> <column name="GID" /> <generator class="increment" /> </id> <property name="gname" type="java.lang.String"> <column name="GNAME" /> </property> </class> </hibernate-mapping>
Syudents.cfg.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-1 19:15:12 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao.Students" table="STUDENTS"> <id name="sid" type="int"> <column name="SID" /> <generator class="increment" /> </id> <property name="sname" type="java.lang.String"> <column name="SNAME" /> </property> <property name="sgender" type="java.lang.String"> <column name="SGENDER" /> </property> <many-to-one name="grader" class="com.third.Dao.Grader" fetch="join"> <column name="GRADER" /> </many-to-one> </class> </hibernate-mapping>
Test1.java
1 package com.third; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 import org.hibernate.Session; 7 import org.hibernate.SessionFactory; 8 import org.hibernate.Transaction; 9 import org.hibernate.cfg.Configuration; 10 import org.hibernate.service.ServiceRegistry; 11 import org.hibernate.service.ServiceRegistryBuilder; 12 import org.junit.After; 13 import org.junit.Before; 14 import org.junit.Test; 15 16 /*import com.third.Dao1.Grader1; 17 import com.third.Dao1.Students1;*/ 18 19 import com.third.Dao.Grader; 20 import com.third.Dao.Students; 21 public class Test1 { 22 23 private static SessionFactory sessionFactory; 24 private static Session session; 25 private static Transaction transaction; 26 @Before 27 public void init(){ 28 //创建配置对象,匹配读取hibernate.cfg.xml文件 29 Configuration config=new Configuration().configure(); 30 //获取服务注册对象(该对象中封装了hibernate.cfg.xml文件中的<properties>、<mapping>信息) 31 ServiceRegistry serviceRegistry=new ServiceRegistryBuilder() 32 .applySettings(config.getProperties()).buildServiceRegistry(); 33 //获取sessionFactory对象(该对象中通过传参serviceRegistry对象,获取了<mapping><properties>信息) 34 sessionFactory=config.buildSessionFactory(serviceRegistry); 35 } 36 37 @Test 38 public void test1(){ 39 //通过sessionFactory对象获取session对象 40 session=sessionFactory.openSession(); 41 //通过session对象开启事务,并返回Transaction对象 42 transaction=session.beginTransaction(); 43 44 Grader grader1=new Grader(); 45 //grader1.setGid(1); 46 grader1.setGname("信息与计算科学一班"); 47 Students student1=new Students(); 48 student1.setSname("小明"); 49 student1.setSgender("男"); 50 student1.setGrader(grader1); 51 52 session.save(grader1); 53 session.save(student1); 54 } 86 @After 87 public void destory(){ 88 //提交事务 89 transaction.commit(); 90 //关闭资源 91 if(session!=null){ 92 session.close(); 93 } 94 if(sessionFactory!=null){ 95 sessionFactory.close(); 96 } 97 } 98 }
运行后数据库的表格样式和表格记录截图:
表格样式:
表格记录:
我们不难看出,对于多对一的映射,其核心的代码是背景为绿色的代码段,即在多方的bean中添加一方的引用,并且在其多方的bean的映射文件hbm.xml中添加<many-to-one>标签进行配置设置。虽然多方hbm.xml文件中没有指出外键,但是我们通过分析表格样式,可以发现eclipse为一方生成了一个与多方主键关联的外键。
3、多对多映射
代码示例:
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.password">tiger</property> <property name="hibernate.connection.url">jdbc:mysql:///hibernate?useUnicode=true&characterEncoding=UTF-8</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="hbm2ddl.auto">create</property> <!-- <mapping resource="com/third/Dao1/Grader1.hbm.xml"/> <mapping resource="com/third/Dao1/Students1.hbm.xml"/> --> <mapping resource="com/third/Dao2/Students2.hbm.xml"/> <mapping resource="com/third/Dao2/Teachers.hbm.xml"/> </session-factory> </hibernate-configuration>
Students2.java
package com.third.Dao2; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Students2 implements Serializable { private int sid; private String sname; private String sgender; private Set<Teachers> teaList=new HashSet<Teachers>(); public Students2() { } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSgender() { return sgender; } public void setSgender(String sgender) { this.sgender = sgender; } public Set<Teachers> getTeaList() { return teaList; } public void setTeaList(Set<Teachers> teaList) { this.teaList = teaList; } }
Teachers.java
package com.third.Dao2; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Teachers implements Serializable { private int tid; private String tname; private String tgender; private Set<Students2> stuList=new HashSet<Students2>(); public Teachers() { } public int getTid() { return tid; } public void setTid(int tid) { this.tid = tid; } public String getTname() { return tname; } public void setTname(String tname) { this.tname = tname; } public String getTgender() { return tgender; } public void setTgender(String tgender) { this.tgender = tgender; } public Set<Students2> getStuList() { return stuList; } public void setStuList(Set<Students2> stuList) { this.stuList = stuList; } }
又eclipse帮助创建的hbm.xml文件(这回这些hbm.xml文件需要进行改动不能进行直接使用)
Students2.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-2 14:57:36 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao2.Students2" table="STUDENTS2"> <id name="sid" type="int"> <column name="SID" /> <generator class="assigned" /> </id> <property name="sname" type="java.lang.String"> <column name="SNAME" /> </property> <property name="sgender" type="java.lang.String"> <column name="SGENDER" /> </property>
<set name="teaList" table="CLASSROOM" inverse="false" cascade="all" lazy="true"> <key> <column name="C_SID" /> </key> <many-to-many class="com.third.Dao2.Teachers" column="C_TID"/> </set> </class> </hibernate-mapping>
Teachers.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-2 14:57:36 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao2.Teachers" table="TEACHERS"> <id name="tid" type="int"> <column name="TID" /> <generator class="assigned" /> </id> <property name="tname" type="java.lang.String"> <column name="TNAME" /> </property> <property name="tgender" type="java.lang.String"> <column name="TGENDER" /> </property> <set name="stuList" table="CLASSROOM" lazy="true"> <key> <column name="C_TID" /> </key> <many-to-many class="com.third.Dao2.Students2" column="C_SID"/> </set> </class> </hibernate-mapping>
Test2.java
1 package com.third; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 import org.hibernate.Session; 7 import org.hibernate.SessionFactory; 8 import org.hibernate.Transaction; 9 import org.hibernate.cfg.Configuration; 10 import org.hibernate.service.ServiceRegistry; 11 import org.hibernate.service.ServiceRegistryBuilder; 12 import org.junit.After; 13 import org.junit.Before; 14 import org.junit.Test; 15 16 import com.third.Dao2.Students2; 17 import com.third.Dao2.Teachers; 18 19 public class Test2 { 20 21 private static SessionFactory sessionFactory; 22 private static Session session; 23 private static Transaction transaction; 24 @Before 25 public void init(){ 26 //先获取配置对象,匹配hibernate.cfg.xml文件 27 Configuration config=new Configuration().configure(); 28 //获取注册服务对象(该对象中包含hibernate.cfg.xml中的<properties>和<maping>信息 29 ServiceRegistry serviceRegistry=new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry(); 30 //获取sessionFactory对象,通过sessionFactory对象读取hibernate.cfg.xml文档信息,并通过<mapping>标签加载hbm.xml文件信息 31 sessionFactory=config.buildSessionFactory(serviceRegistry); 32 } 33 34 @Test 35 public void test2(){ 36 //通过sessionFactory对象获取session对象 37 session=sessionFactory.openSession(); 38 //通过session对象开启事务,并且返回事务(transaction)对象 39 transaction=session.beginTransaction(); 40 41 //实例Students2对象,完成基本赋值 42 Students2 stu1=new Students2(); 43 stu1.setSid(1); 44 stu1.setSname("小美"); 45 stu1.setSgender("女"); 46 47 Students2 stu2=new Students2(); 48 stu2.setSid(2); 49 stu2.setSname("小泽"); 50 stu2.setSgender("男"); 51 52 Students2 stu3=new Students2(); 53 stu3.setSid(3); 54 stu3.setSname("小敏"); 55 stu3.setSgender("女"); 56 57 //实例Teacher对象,完成基本赋值 58 Teachers tea1=new Teachers(); 59 tea1.setTid(1); 60 tea1.setTname("王老师"); 61 tea1.setTgender("男"); 62 63 Teachers tea2=new Teachers(); 64 tea2.setTid(2); 65 tea2.setTname("马老师"); 66 tea2.setTgender("男"); 67 68 //我们通过Students2对象,进行Students2与Teachers的级联关系储存操作 69 //其中tea1和tea2都是stu1的老师 70 stu1.getTeaList().add(tea1); 71 stu1.getTeaList().add(tea2); 72 73 //其中tea1和tea2都是stu2的老师 74 Set<Teachers> teaList=new HashSet<Teachers>(); 75 teaList.add(tea1); 76 teaList.add(tea2); 77 78 //其中tea1是stu3的老师 79 stu3.getTeaList().add(tea1); 80 81 //保存这些对象(这里涉及到级联,其中维护方为Students2) 82 session.save(stu1); 83 session.save(stu2); 84 session.save(stu3); 85 86 } 87 88 @After 89 public void destory(){ 90 transaction.commit(); 91 //关闭开启的资源 92 if(session!=null){ 93 session.close(); 94 } 95 if(sessionFactory!=null){ 96 sessionFactory.close(); 97 } 98 } 99 }
运行后数据库的表格样式和表格记录:
表中记录:
我们不难发现绿色背景的代码是多对多映射实现的核心代码,即在两个bean都需要在各自bean中添加对方的引用集合,但是他们的由eclipse生成的hbm.xml文件不能直接使用,不像一对多和多对一的生成的hbm.xml代码不要做修改就可使用。因为eclipse帮忙生成的两个hbm.xml文件中是生成<many-to-one>标签的,如果不修改直接使用的话,将会在数据库生成两个表格,这两个表格各自有一个外键,而且每个表的各自的外键分别与另一个表的主键关联,理论上这种思路也行,也可以实现多对多的关系映射,但是这样面临的结果是记录的插入和更新修改将会受到很多限制,很容易报错,举一个例子:如果我们想要直接删掉这两个表格,将会有外键的限制,不管先删除谁都不好使,只能在解除外键约束后才能删除。多对多映射的正确思路是两个多方表格,通过第三个表格,这个表格拥有两个外键分别与两个多方表格的主键相对应,通过第三个表格来存储多对多关系映射。这样我们就需要修改eclipse帮我们生成的两个hbm.xml文件,使用<many-to-many>标签,并指出第三个表格的表格名。如下图:
4、总结
(1)一对多映射,我们是通过在一方的bean中添加多方的引用集合,并在一方的hbm.xml文件中配置<one-to-many>标签及相关信息。
(2)多对一映射,我们是通过在多方的bean中添加一方的引用,并在多方的hbm.xnl文件中配置<many-to-many>标签及相关信息。
(3)多对多映射,我们是通过在两个多方的bean中都添加对方的引用集合,并在两个多方的hbm.xml文件中配置<many-to-many>标签及相关信息。
(4)不管是一对多还是多对一的映射,由eclipse运行后都会在多方的表格中添加一个外键,而且这个外键与一方的主键关联,区别在于:一对多映射多方的hbm.xml文件中会指明外键名,而多对一映射不会指出,但是查看生成的表格会看到多方的表格被添加了外键并且关联与一方表格的主键。然而多对多映射,是在第三个表格设置两个外键,这连个外键分别关联着两个多方的表格的主键,且这个第三方表格的主键是由这两个外键联合而成的复合主键。
(5)不管是多对一还是一对多映射,其通过eclipse帮助创建生成的hbm.xml文件可以直接使用,可以不需要改动。但是多对多映射时,其通过eclipse帮助创建说呢过成的hbm.xml文件需要进行一些修改,不能直接使用。
(6)既然涉及到映射关系,那么生成的表格会存在这级联关系,我们可以通过inverse和cascade设置级联的维护方和级联操作的范围,当表格的更新和添加等操作,在表格中显示的信息不全时,我们可以考虑一下级联。