hibernate--关系映射归纳和总结
来源:互联网 发布:复杂网络中的幂律分布 编辑:程序博客网 时间:2024/04/30 21:17
有用的参考文章:
http://blog.csdn.net/laner0515/article/details/12905711 重要
http://blog.csdn.net/linminqin/article/details/6324567
http://www.cnblogs.com/jyh317/p/3691842.html
http://blog.csdn.net/lovelyhermione/article/details/2020881
前言:
hibernate关系映射十分重要,对于掌握hibernate是必备的知识,关系映射分为一对一 一对多 多对一 多对多 看起来关系关系复杂,但其实了解其中原理就可以灵活自如运用。在了解详细分类之前,有一些点和关键系必须掌握其意义:
cascade关键词:表示级联包括add update delete 一般情况不使用 不好。 作用:一般在保存对象时 需要先显示的save对端关联的对象 变为持久太之后才可以在被本端set进去,但在配置文件中加入级联之后 可以不显示的save,我们save本端对象后(set完对端) 自动save关联对象。最佳实践: 如果没有特殊情况 不要使用cascade 特别注意:可能使用cascade的地方一般都是在一的一方进行删除时候 多的一方删除会出问题
特殊需求才会使用cascade的add 正常情况add方法都是应该由程序员添加(为了防止级联添加乱七八糟的其他东西)
inverse:默认为false 。设置为true表示让对端来进行维护关系 即让对端来set。
lazy=extra:使用了lazy=extra之后会稍微只能些 会根据取得值得不同来判断是调用的count还是获取投影
property-ref:xml文件中主表端property-ref表示一对一时让对端(从表端)维护关系
1: 所谓哪端维护关系指含有对方引用的一端 可以对对端的对象set操作 可能由一端维护 也可能由多的一端维护
如果有设置了Inverse=true 或 property-ref(1对1)的话 维护关系端变成对端
所谓单双向 就是指在哪端加对端的引用
2: 在哪端添加时 要先把关联的对端对象给save了 不允许 不save 因为那是瞬时态,否则说瞬时对象没有被保存
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing但是有一种情况 就是xml配置时 加入cascade 可以不显示的save关联对象 但这在多对一级联删除多端某个对象时又出现问题 因为级联删除一端后 相同外键的多端值就错了
3:所谓One to many 双向 就是 one to many 单向加many to one 单向
4:注意:即使是双向设置 即两边都加了引用 但如果我在添加时 在一的一方添即维护关系 只在一端set了多端 而多端没有把一端set进去
此时仍然会发出5条语句 三个插入 两个更新 因为需要更新外键值
5:注意:如果在一端加入了inverse=true 表示让对端(多端)维护关系 如果仍然像4中在一端set多端的值
此时只会产生3条插入语句 不会有更新语句 多端外键的则值为Null 因为多端根本不知道一端的情况即多端没有set一端的classroom值
6:一句话 如果让多端维护关系 那么添加时需要从多端添加 把一端的对象给set进去 才能正确
如果让一端维护关系 那么添加时需要从一端添加set多端的值 但此时效率低 多产生update语句
7:一端加载时 可以把多的也加载出来 同理 多端加载时 可以把一端也加载进来(只要设置了关系即有了对方的引用即可) 注意 查询是不用级联的 与级联无关 用load时候 会自动懒加载关联对象(不会产生SQL语句 在调用它的属性时候才会) get会自动加载关联对象(产生SQL语句)
1) 加载和cascade是没有关系的 cascade只有增加 删除 修改
2) 加载也与Inverse没有关系 只要在某一端配置了关系 就可以进行加载对端或者懒加载
8:重要:关联加载产生的条件:
只要本端持有对端的对象 也就是本端也维护关系 那么就可以用Load或者get来关联调用对方对象
注意:在一对一双向关联时最特殊(延迟加载在其他关联时都正常):在先加载非维护关系端的对象时,
取维护关系一端的对象才能实现对端的延迟加载 如果取得是不维护关系的一端 那么只会发出1条语句(用join)
把关联对象全部查询 不会延迟加载
9:一对一的时候 最好不要使用双向关联
如果使用双向关联 尽可能在没有维护关系的一边取数据
hibernate会自动完成join操作 仅仅只会发出一条SQL
如果从维护关系端取数据 在通过延迟加载取关联对象时 会同时再去取person的IDcard关联
10:在双向关系中 需要加一个设置 让某一端来进行维护 比如一对一时候 在主表端添加property-ref让从表端维护 一对多双向时候 设置inverse=true
11:多对多时 使用关联对象以及关联表(两个一对多关系) 不要单独使用关联表
12:总体来说:最佳实践就是,一般不使用双向关联,特别不建议使用一的这一方的关联
在设计的时候不是特殊情况不要使用双向关联
一:多对一单向
配置文件:
hibernate.cfg.xml<!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.dialect">org.hibernate.dialect.MySQLDialect</property><property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property><property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property><property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hiber?characterEncoding=UTF8</property><property name="hibernate.connection.username">root</property><property name="hibernate.connection.password"></property><property name="show_sql">true</property><!-- <property name="format_sql">true</property>--><property name="hibernate.hbm2ddl.auto">update</property><mapping resource="com/itany/model/User.hbm.xml"/><mapping resource="com/itany/model/Book.hbm.xml"/><mapping resource="com/itany/model/Classroom.hbm.xml"/><mapping resource="com/itany/model/Student.hbm.xml"/><mapping resource="com/itany/model/Message.hbm.xml"/><mapping resource="com/itany/model/Comment.hbm.xml"/></session-factory></hibernate-configuration>
Classroom.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Classroom" table="t_cla"><id name="id" ><!-- 主键的生成策略 native increment assigned--> <generator class="native"></generator></id><property name="name" ></property><property name="grade" ></property></class></hibernate-mapping>
Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Student" table="t_stu"><id name="id" ><!-- 主键的生成策略 native increment assigned--> <generator class="native"></generator></id><property name="name" ></property><property name="no" ></property><!-- 当设置cascade时候 会自动完成关联 若添加(写)时没有显示的添加关联对象(此处是之前写入父关联对象classroom) 那么会自动添加一个关联对象 最佳实践: 如果没有特殊情况 不要使用cascade 特别注意:可能使用cascade的地方一般都是在一的一方进行删除时候 多的一方删除会出问题特殊需求才会使用cascade的add 正常情况add方法都是应该由程序员添加(为了防止级联添加乱七八糟的其他东西)--><many-to-one name="classRoom" class="Classroom" column="cid" cascade="all"></many-to-one></class></hibernate-mapping>
junit4测试代码:
public class TestMany2One{ /* * 多对一单向 */ @Test public void testAdd1() { Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); //先添加一的一方 再添加多的一方 关系由多的一方维护 共发出三条SQL语句 Classroom c=new Classroom(); c.setGrade(2016); c.setName("计算机"); session.save(c); Student stu1=new Student(); stu1.setName("张三"); stu1.setNo("001"); stu1.setClassRoom(c); Student stu2=new Student(); stu2.setName("张三2"); stu2.setNo("003"); stu2.setClassRoom(c); session.save(stu1); session.save(stu2); trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testAdd2() { Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); //先添加多的一方 再添加一的一方 此时要多修改两次 发出5条语句 Student stu1=new Student(); stu1.setName("张三3"); stu1.setNo("001"); session.save(stu1); Student stu2=new Student(); stu2.setName("张三4"); stu2.setNo("003"); session.save(stu2); Classroom c=new Classroom(); c.setGrade(2017); c.setName("计算机"); session.save(c); stu1.setClassRoom(c); stu2.setClassRoom(c); /* * 最佳实践 一定要先添加一的一方 再添加多的一方 总之关系让多的一方来维护 */ trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testAdd3WithoutCascade() { /* * 注意;查询是不用级联的 与级联无关 用load时候 会自动懒加载 get会自动加载 * 在多的配置文件中没有加入cascade 也没有保存classroom 直接保存student * 在保存student时 是需要联系外键的 但此时 没有显示的保存classsroom 因此student和classroom都无法保存 * 抛出异常 * org.hibernate.TransientObjectException: * object references an unsaved transient instance - save the transient instance before flushing: com.itany.model.Classroom */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); Classroom c=new Classroom(); c.setGrade(2018); c.setName("计算机科学"); Student stu1=new Student(); stu1.setName("张三5"); stu1.setNo("001"); session.save(stu1); Student stu2=new Student(); stu2.setName("张三6"); stu2.setNo("003"); session.save(stu2); stu1.setClassRoom(c); stu2.setClassRoom(c); trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testAdd4WithCascade() { /* * 不需要显示保存关联对象了 */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); Classroom c=new Classroom(); c.setGrade(2018); c.setName("计算机科学"); Student stu1=new Student(); stu1.setName("张三5"); stu1.setNo("001"); session.save(stu1); Student stu2=new Student(); stu2.setName("张三6"); stu2.setNo("003"); session.save(stu2); stu1.setClassRoom(c); stu2.setClassRoom(c);//发出五条语句 前三条为插入对象 后两条为修改stu对象 trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testLoad() { /* * 多对一 加载多的对象 可以把一的对象也加载出来(多端维护关系) */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); Student s1=(Student)session.load(Student.class,1);//这一步没有SQL产生 System.out.println(s1.getName());//此时仅仅发出1条SQL 并没有加载classroom对象 查询出的是代理 懒加载 System.out.println(s1.getClassRoom().getName());//此时又发出一条sql 才真正查询classroom对象 trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testLoad2() { /* * 单向多对一 加载一端的对象 (多端维护关系)这种从一端加载多端的对象是不可能的 */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); Classroom c1=(Classroom)session.load(Classroom.class,1);//这一步没有SQL产生 SQL产生在c1.getName()的时候 //Classroom c1=(Classroom)session.get(Classroom.class,1);//这一步有SQL产生 System.out.println(c1.getName()); } catch (Exception e) { if(null!=session) e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testDeleteWithCascade() { /* * 在多的一方进行级联删除 配置文件已加cascade=all * 当有多个多的一方外键只想同一个一的对象 * 在级联删除多的某一个时 想把一的一方也级联删除 会异常 * Cannot delete or update a parent row: a foreign key constraint * 因为多的一方还有其他的行关联了同一个一的一方的对象 */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); Student s=(Student)session.load(Student.class,11); session.delete(s); trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } }}
二:一对多单向
一对多单向 开发中不建议使用 麻烦 效率低
Message.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Message" table="t_message"><id name="id" ><!-- 主键的生成策略 assigned 开发人员指定 --> <!-- <generator class="assigned"></generator> --><!-- 主键的生成策略 uuid会自动生成字符串16进制 128位 16字节 注意此时主键必须为String类型 --> <generator class="native"></generator></id><property name="title" ></property><property name="content" ></property><!-- 使用了lazy=extra之后会稍微只能些 会根据取得值得不同来判断是调用的count还是获取投影 --><set name="comments" lazy="extra"><!-- key 指的是(对方)多的一方的外键名称 此处为one to many 单向--><key column="mid" /><one-to-many class="Comment"/></set></class></hibernate-mapping>
Comment.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Comment" table="t_comment"><id name="id" ><!-- 主键的生成策略 assigned 开发人员指定 --> <!-- <generator class="assigned"></generator> --><!-- 主键的生成策略 uuid会自动生成字符串16进制 128位 16字节 注意此时主键必须为String类型 --> <generator class="native"></generator></id><property name="content" ></property></class></hibernate-mapping>
junit4测试
public class TestOne2Many{ /* * 一对多单向 * 开发中不建议使用 麻烦 效率低 */ @Test public void testAdd() { Session session=null; Transaction trans=null; try { session=HibernateUtil.openSession(); trans=session.beginTransaction(); Comment c1=new Comment(); c1.setContent("123"); Comment c2=new Comment(); c2.setContent("456"); session.save(c1); session.save(c2); Message m1=new Message(); m1.setTitle("圣兽山"); m1.setContent("sssssssssssssssssssssssss"); m1.addComments(c1); m1.addComments(c2); session.save(m1); trans.commit(); /* * 一共发出5条SQL语句 三个Insert 两个Update 因为在一的一方维护关系(一种有多方的引用 一方来set) * 多的一方无法知道主表是什么 因为多的一方没有一方的引用 * 因此 一的一方保存后 为了通过One2many维护关系 需要通过外键update2次 在一方维护效率低下 */ } catch (HibernateException e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testLoad() { /* * 一端加载时 可以把多的也加载出来 同理 多端加载时 可以把一端也加载进来 * 1 加载和cascade是没有关系的 cascade只有增加 删除 修改 * 2 加载也与Inverse没有关系 只要在某一端配置了关系 就可以进行加载对端或者懒加载 */ Session session=null; session=HibernateUtil.openSession(); Message m1=(Message)session.load(Message.class,1); System.out.println(m1.getContent()); Set<Comment> comments=m1.getComments(); for (Comment comment : comments) { //懒加载 System.out.println(comment.getContent()); } try { session=HibernateUtil.openSession(); } catch (HibernateException e) { e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testLoad2() { /* * 在set标签中添加 lazy="extra" 使得我们调用set.size()方法时候 SQL用count()稍微智能 * <set name="comments" lazy="extra"> */ Session session=null; session=HibernateUtil.openSession(); Message m1=(Message)session.load(Message.class,1); System.out.println(m1.getContent()); Set<Comment> comments=m1.getComments(); System.out.println(comments.size()); try { session=HibernateUtil.openSession(); } catch (HibernateException e) { e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } }}
主要还是看谁来维护关系 即让哪端进行添加操作等
如果在这种设置下,代码中让一端维护关系 在配置文件中一端inverse=true 让多端配置
那么会造成多端的外键值为Null
Classroom.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Classroom" table="t_cla"><id name="id" ><!-- 主键的生成策略 native increment assigned--> <generator class="native"></generator></id><property name="name" ></property><property name="grade" ></property><set name="students" lazy="extra" inverse="true"><key column="cid"/><one-to-many class="Student"/></set></class></hibernate-mapping>
Student.hbm.xml
</pre><pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Student" table="t_stu"><id name="id" ><!-- 主键的生成策略 native increment assigned--> <generator class="native"></generator></id><property name="name" ></property><property name="no" ></property><!-- 当设置cascade时候 会自动完成关联 若添加(写)时没有显示的添加关联对象(此处是之前写入父关联对象classroom) 那么会自动添加一个关联对象 最佳实践: 如果没有特殊情况 不要使用cascade 特别注意:可能使用cascade的地方一般都是在一的一方进行删除时候 多的一方删除会出问题特殊需求才会使用cascade的add 正常情况add方法都是应该由程序员添加(为了防止级联添加乱七八糟的其他东西)--><many-to-one name="classRoom" class="Classroom" column="cid" cascade="all"></many-to-one></class></hibernate-mapping>
junit4测试
@Test public void testAdd3() { /* * 所谓维护关系 就是指在哪端添加 所谓单双向 就是指在哪端加对端引用 * 在一端维护 表示在一端set进多端的值 * 在多端维护 表示在多端set进一端的值 * * 注意:在哪端添加时 要先把关联的对端对象给save了 不允许 不save 因为那是顺势态 * * 所谓One to many 双向 就是 one to many 单向加many to one 单向 * 1 注意:即使是双向设置 即两边都加了引用 但如果我在添加时 在一的一方添即维护关系 只在一端set了多端 而多端没有把一端set进去 * 此时仍然会发出5条语句 三个插入 两个更新 因为需要更新外键值 * * 2 注意:如果在一端加入了inverse=true 表示让对端(多端)维护关系 如果仍然像情况1中在一端set多端的值 * 此时只会产生3条插入语句 不会有更新语句 多端外键的则值为Null 因为多端根本不知道一端的情况即多端没有set一端的classroom值 * * 一句话 如果让多端维护关系 那么添加时需要从多端添加 把一端的对象给set进去 才能正确 * 如果让一端维护关系 那么添加时需要从一端添加set多端的值 但此时效率低 多产生update语句 */ Session session=null; Transaction trans=null; try { session=HibernateUtil.openSession(); trans=session.beginTransaction(); Student s1=new Student(); s1.setName("lisi3"); s1.setNo("333");//注意!!! 瞬时对象要先session.save 然后在被set进classroom对象中 否则报错 说瞬时对象没有被保存 //org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing session.save(s1); Student s2=new Student(); s2.setName("lisi4"); s2.setNo("444"); session.save(s2); Set<Student> students=new HashSet<Student>(); students.add(s1); students.add(s2); Classroom c=new Classroom(); c.setName("C++班"); c.setGrade(2019); c.setStudents(students); session.save(c); trans.commit();//5条SQL /* * 一共发出5条SQL语句 三个Insert 两个Update 因为在一的一方维护关系(一种有多方的引用 一方来set) * 多的一方无法知道主表是什么 因为多的一方没有一方的引用 * 因此 一的一方保存后 为了通过One2many维护关系 需要通过外键update2次 在一方维护效率低下 */ } catch (HibernateException e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testAdd4() { /* * 与testAdd3对应 这是双向 时限制在多端进行维护(inverse) 先把一端关联的给save了 */ Session session=null; Transaction trans=null; try { session=HibernateUtil.openSession(); trans=session.beginTransaction(); Classroom c=new Classroom(); c.setName("C班"); c.setGrade(2020); session.save(c); Student s1=new Student(); s1.setName("lisi5"); s1.setNo("555");//注意 瞬时对象要先session.save 然后在被set进classroom对象中 否则报错 说瞬时对象没有被保存 //org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing s1.setClassRoom(c); session.save(s1); Student s2=new Student(); s2.setName("lisi6"); s2.setNo("666"); s2.setClassRoom(c); session.save(s2); trans.commit();//5条SQL /* * 一共发出5条SQL语句 三个Insert 两个Update 因为在一的一方维护关系(一种有多方的引用 一方来set) * 多的一方无法知道主表是什么 因为多的一方没有一方的引用 * 因此 一的一方保存后 为了通过One2many维护关系 需要通过外键update2次 在一方维护效率低下 */ } catch (HibernateException e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testLoad3() { /* *环境是 一对多 双向 两端都进行了配置 同上 */ Session session=null; session=HibernateUtil.openSession(); Classroom c=(Classroom)session.load(Classroom.class,7);//从一端加载 System.out.println(c.getStudents().size()); Student s=(Student)session.load(Student.class,17);//从多端加载 System.out.println(s.getName()); System.out.println(s.getClassRoom().getName());//此处不会产生SQL 用的是session缓存 //以上一共3条SQL try { session=HibernateUtil.openSession(); } catch (HibernateException e) { e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } }
四:一对一单向(从表端进行维护关系)
主表:person.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Person" table="t_person"><id name="id" column="id"><!-- 主键的生成策略 native increment assigned--> <generator class="native"></generator></id><property name="name" /></class></hibernate-mapping>
从表:idcard.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="IDCard" table="t_id_card"><id name="id" column="id"><generator class="native"></generator></id><property name="no" ></property><!-- 一对一外键关联 --><many-to-one name="person" column="pid" unique="true" /></class></hibernate-mapping>
junit4测试:
public class TestOne2One{ //person是主表 idcard是从表 //-----------------------------------以下为一对一单向(由外键即从表端维护关系)------------------------------------------ @Test public void testAdd() { Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); Person p=new Person(); p.setName("隔壁老王"); session.save(p); IDCard card=new IDCard(); card.setNo("111111"); card.setPerson(p); session.save(card); trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testAdd02() { /* * 由于使用了Unique 为一对一单向外键关联 此时添加两个一端绑定同一个Person 包重复错误 */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); Person p=(Person)session.load(Person.class,1); IDCard card=new IDCard(); card.setNo("22222222"); card.setPerson(p); session.save(card); trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testload1() { Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); //从多的一端进行延迟加载 /* * 所谓哪端维护关系指含有对方引用的一端 可以对对端的对象set操作 可能由一端维护 也可能由多的一端维护 * 如果有设置了Inverse=true 或 property-ref(1对1)的话 维护关系端变成对端 * * 重要:延迟加载产生的条件: * 1 加载时候使用Load方法 * 2 取维护关系一端的对象才能实现对端的延迟加载 如果取得是不维护关系的一端 那么只会发出1条语句(用join) * 把关联对象全部查询 不会延迟加载 * * 因此这个一对一单向 从表端维护 可以做到延迟加载 会产生2条SQL语句 */ IDCard card=(IDCard)session.load(IDCard.class,5); System.out.println(card.getNo()+","+card.getPerson().getName()); } catch (Exception e) { e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } }
}
如果一定要使用 在查询时候 要从不维护关系的一端进行查询 这样只会产生1条语句 ,若从维护关系的一端查询会产生3条语句 效率低
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Person" table="t_person"><id name="id" column="id"><!-- 主键的生成策略 native increment assigned--> <generator class="native"></generator></id><property name="name" /><!-- name表示属性的名次 property-ref表示由对端来维护关系 也就是对端的person属性维护关系 --><one-to-one name="card" property-ref="person" /></class></hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="IDCard" table="t_id_card"><id name="id" column="id"><generator class="native"></generator></id><property name="no" ></property><!-- 一对一外键关联 --><many-to-one name="person" column="pid" unique="true" /></class></hibernate-mapping>
测试代码:
//-----------------------------------以下为一对一双向(两边都进行了配置)------------------------------------------ @Test public void testAdd3() { /* * 重要 xml文件中主表端property-ref表示一对一时让对端(从表端)维护关系 因此 * 下面的添加操作 虽然我们是在主表端进行的 但是从表端的外键值为null 关系不会更新(因为添加代码没有从从表端维护 即没有从表.set(主表对象)) * 和在双向一对多关联时 一端Inverse时发生的情形一样 */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); IDCard card=new IDCard(); card.setNo("3333"); session.save(card); Person p=new Person(); p.setName("隔离老张"); p.setCard(card); session.save(p); trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testAdd4() { /* * 一对一双向都配置时 主表端配置了property-ref 表示让对端从表进行维护 * 我们这里从从表外键端进行添加维护 完全正确 */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); trans = session.beginTransaction(); Person p=new Person(); p.setName("lao liu"); session.save(p); IDCard card=new IDCard(); card.setNo("6666"); card.setPerson(p); session.save(card); trans.commit(); } catch (Exception e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testload2() { /* * 一对一双向时 从没有维护关系的一端(此时是主表端 因为主表端设置了property-ref表示对端维护)进行加载 * 此时会产生一条SQL语句 不会延迟加载 * 因为延迟加载是由维护关系的一端也就是外键端进行的 */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); Person p=(Person)session.load(Person.class,1); System.out.println(p.getName()+p.getCard().getNo()); } catch (Exception e) { e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testload3() { /*这个地方比较特殊: * 一对一双向时 从维护关系的一端 此处为从表端进行取从表对象 会发3条 * 第一条为从表对象 第二条为把懒加载的主表对象取出 因为主表关联从表 此时又会发一条去取idcard 共3条 * * 特别注意:如果没有双向,此时会发两条 一条取IDcard 一条延迟加载person * */ Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); IDCard card=(IDCard)session.load(IDCard.class,5); System.out.println(card.getNo()+","+card.getPerson().getName()); } catch (Exception e) { e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } /* * 最佳实践: * 一对一的时候 最好不要使用双向关联 * 如果使用双向关联 尽可能在没有维护关系的一边取数据 * hibernate会自动完成join操作 仅仅只会发出一条SQL * 如果从维护关系端取数据 在通过延迟加载取关联对象时 会同时再去取person的IDcard关联 */
六:多对多(三张表 两个类 使用关联表 不推荐 因为很多字段只能存在关联表中 又没有对应的类对应 很是不方便)
admin 表示用户 role表示角色 是多对多的关系
Admin.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Admin" table="t_admin"><id name="id" > <generator class="native"></generator></id><property name="name" /><set name="roles" table="t_admin_role" lazy="extra"><key column="aid" /><!-- 是我在对方的外键 在别人的表里--><many-to-many class="Role" column="rid"/></set> </class></hibernate-mapping>
Role.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Role" table="t_role"><id name="id" > <generator class="native"></generator></id><property name="name" /><set name="admins" table="t_admin_role" lazy="extra"><key column="rid" /><many-to-many class="Admin" column="aid"/></set></class></hibernate-mapping>
测试代码:
/* * 使用ManyToMany(两个实体类 三个表 其中一个为关联表)无论从哪端来维护关系都比较麻烦 * 而且很多属性只能加在关联表中 比如学生-课程关系 学生每门课的成绩就必须保存在关联表中 而在实体类中没有类与之对应 * * 所以在开发中,经常是使用两个一对多的关系来代替多对多(三个实体类 三张表) */ //------------------------------------两个实体类 三个表 其中一个为关联表------------------------------------------------ @Test public void testAdd() { Session session=null; Transaction trans=null; try { session=HibernateUtil.openSession(); trans=session.beginTransaction(); Admin a1=new Admin(); a1.setName("张三"); session.save(a1); Admin a2=new Admin(); a2.setName("李四"); session.save(a2); Role r1=new Role(); r1.setName("超级管理员"); r1.add(a1); r1.add(a2); session.save(r1); Role r2=new Role(); r2.setName("财务管理员"); r2.add(a1); r2.add(a2); session.save(r2); //以上共产生8条语句 //2条admin插入 2条role插入 //4条中间表插入 trans.commit(); } catch (HibernateException e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testLoad() { Session session=null; Transaction trans=null; try { session = HibernateUtil.openSession(); Admin a1=(Admin)session.load(Admin.class,1); for (Role role : a1.getRoles()) { System.out.println(role.getName()); }//以上产生两条语句 } catch (Exception e) { e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } }
七:多对多(三张表 三个类 两个一对多关系的合并 推荐使用)
teacher和course的关系 使用TeacherCourse作为关联对象 且有对应的关联表
teacher.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Teacher" table="t_teacher"><id name="id" > <generator class="native"></generator></id><property name="name" /><set name="tcs" lazy="extra" inverse="true"><key column="tid" /><one-to-many class="TeacherCourse"/></set> </class></hibernate-mapping>course.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="Course" table="t_course"><id name="id" > <generator class="native"></generator></id><property name="name" /><set name="tcs" lazy="extra" inverse="true"><key column="cid" /><one-to-many class="TeacherCourse" /></set> </class></hibernate-mapping>teachercourse.hbm.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.itany.model"><class name="TeacherCourse" table="t_teacher_course"><id name="id" > <generator class="native"></generator></id><property name="score" /><many-to-one name="teacher" column="tid" class="Teacher" /> <many-to-one name="course" column="cid" class="Course" /></class></hibernate-mapping>TeacherCourse.java
public class TeacherCourse{ private int id; private double score; private Teacher teacher; private Course course;
//set get方法
}测试类:
//------------------------------------三个实体类 三个表 两个一对多关系的合并------------------------------------------------ @Test public void testAdd2() { Session session=null; Transaction trans=null; try { session=HibernateUtil.openSession(); trans=session.beginTransaction(); Teacher t1=new Teacher(); t1.setName("张三"); session.save(t1); Teacher t2=new Teacher(); t2.setName("李四"); session.save(t2); Course c1=new Course(); c1.setName("java"); session.save(c1); Course c2=new Course(); c2.setName("c++"); session.save(c2); TeacherCourse tc1=new TeacherCourse(); tc1.setScore(99); tc1.setTeacher(t1); tc1.setCourse(c1); session.save(tc1); TeacherCourse tc2=new TeacherCourse(); tc2.setScore(98); tc2.setTeacher(t1); tc2.setCourse(c2); session.save(tc2); TeacherCourse tc3=new TeacherCourse(); tc3.setScore(97); tc3.setTeacher(t2); tc3.setCourse(c1); session.save(tc3); TeacherCourse tc4=new TeacherCourse(); tc4.setScore(96); tc4.setTeacher(t2); tc4.setCourse(c2); session.save(tc4); trans.commit(); } catch (HibernateException e) { if(null!=session) trans.rollback(); e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } } @Test public void testLoad2() { Session session=null; Transaction trans=null; try { //共产生4条SQL session = HibernateUtil.openSession(); Teacher t=(Teacher)session.load(Teacher.class, 1);//这步执行完并没有产生SQL for (TeacherCourse tc:t.getTcs())//第一次到这会产生两条SQL语句 { System.out.println(tc.getCourse().getName()+","+tc.getScore());//每次到这都会延迟加载一次 } /* * load的时候由于延迟加载 会根据不同的情况取相应的关联对象 所以会发出大量的sql * * 总体来说:最佳实践就是,一般不使用双向关联,特别不建议使用一的这一方的关联 * 因为从一的这一端取关联对象很有可能涉及到分页操作(一次就要取出大量对象 不能使用延迟加载),所以基本不会使用 * * 在设计的时候不是特殊情况不要使用双向关联 */ } catch (Exception e) { e.printStackTrace(); } finally { if(null!=session) HibernateUtil.closeSession(session); } }
把hibernate关联映射总结下,可终结。也为日后复习方便。
- hibernate--关系映射归纳和总结
- Hibernate关系映射总结
- hibernate映射关系总结
- hibernate关系映射总结
- Hibernate关系映射总结
- 【Hibernate】映射关系总结
- 【Hibernate】映射关系总结
- Hibernate关系映射总结(一)
- Hibernate关系映射总结(二)
- Hibernate关系映射总结(三)
- Hibernate映射关系总结篇
- Hibernate 关系映射 总结整理
- hibernate关系映射总结篇
- Hibernate 关系映射 总结整理
- Hibernate 关系映射 总结整理
- Hibernate 关系映射 总结整理
- Hibernate常见映射关系总结
- Hibernate 关系映射 总结整理
- iOS与Swift的那一个NS(Mutable)AttributedString
- Android ListView显示底部的分割线
- 自学Python之遍历字典
- Quartz的cron表达式
- 评教系统优化之使用事务批量导入DataTable
- hibernate--关系映射归纳和总结
- 项目进度计划
- ListView 适配器实现getviewtypecount() 数组越界IndexOutOfBoundException
- leetcode笔记:Pow(x, n)
- maven打tar.gz zip包—maven-assembly-plugin
- [知其然不知其所以然-15] cgroup概述
- MyBatis项目-shop购物系统
- Oracle高级查询,GROUP BY
- Xcode7中 http请求报错App Transport Security has blocked a cleartext HTTP