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关联映射总结下,可终结。也为日后复习方便。



0 0