hibernate--关系映射归纳和总结

有用的参考文章:

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值