关联映射之多对一

一、关联映射之多对一


        对于多对一关联映射其实很容易理解,在思考时可以把它看做人员和组之间的关系,在一个组中会有多个人员,所以这就出现了多对一的关系多个人会同属于一个组,那么在设计关系模型时就会有两种设计方法,一种是将组号作为外键添加到用户表中,另外一种是单独生成第三张表,将用户id号和组id号相关联。对于第一种设计方法它的关系模型可用下表表示:


       这种多对一关联映射反应到对象模型中它是一种聚合关系,User是group的一部分,group中存在User,它们两个的生命周期是不相同的,可反应为下图:


      那么这种多对一关系映射在Hibernate中是如何设置的呢?下面将会介绍两种方法,使用<many-to-one>标签直接映射,或者使用<many-to-one>的cascade级联修改表。


  1、Many-to-one直接映射


       从字面意思上就能够理解,它是指多对一的关系,many指的是多的一端,one指的是少的一端,在使用时往往在多的一端的hbm中使用该标签,并将<many-to-one>的name属性设置为该映射文件对应的类中的one一端的属性,如:<many-to-one name="group" column="groupid"></many-to-one>,该标签添加在了User.hbm.xml中,它对应many;标签中的name值为group对映射one,并且在User.java中会有一个名为group的属性。接下来看下具体实现实现的代码类。

    (1)User.java类代码,其中有一个名为group的属性,它将会作为<many-to-one>的one一端的name值。


  1. public class User {  
  2.     private String name;  
  3.     public String GetName(){  
  4.         return name;  
  5.     }  
  6.     public void SetName(String name){  
  7.         this.name=name;  
  8.     }  
  9.       
  10.     private Group group;  
  11.     public Group GetGroup(){  
  12.         return group;  
  13.     }  
  14.     public void SetGroup(Group group){  
  15.         this.group=group;  
  16.     }  
  17. }  

       (2)User.hbm.xml中的<many-to-one>,name的值为User.java中one端的属性值,它会在数据库中生成一个新列,可以将该新列理解为User表的外键。
  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  4. <!-- Generated 2014-5-14 23:39:25 by Hibernate Tools 3.4.0.CR1 -->  
  5. <hibernate-mapping>  
  6.     <class name="com.hibernate.User" table="USER">  
  7.         <id name="id" type="java.lang.Long">  
  8.             <column name="ID" />  
  9.             <generator class="assigned" />  
  10.         </id>  
  11.           
  12.         <!-- name的值group为User.java中的一个对应的one中的一个属性,它会自动在表中生成一列,所以使用column对列进行了重命名 -->  
  13.         <many-to-one name="group" column="groupid"></many-to-one>  
  14.           
  15.     </class>  
  16. </hibernate-mapping>  


     (3)测试上面的映射关系,向表中写入两个User对象分别为user1和user2,命名为张三和李四,使用session保存对象,向数据库中写入数据,代码如下:


  1. public void testSave1(){  
  2.           
  3.         Session session=null;  
  4.         try{  
  5.             session=GetSession.getSession();  
  6.             session.beginTransaction();  
  7.               
  8.             Group group=new Group();  
  9.             group.SetName("动力节点");  
  10.               
  11.             User user1=new User();  
  12.             user1.SetName("张三");  
  13.             user1.SetGroup(group);  
  14.               
  15.             User user2=new User();  
  16.             user2.SetName("李四");  
  17.             user2.SetGroup(group);  
  18.               
  19.             session.save(user1);  
  20.             session.save(user2);  
  21.               
  22.             //会报TransientObjectException错误  
  23.             //在清理缓存时发生错误TransientObjectException  
  24.             //因为Group为Transient状态,没有被Session,在数据库中没有匹配的数据  
  25.             //而User为Persistent状态,在清理缓存时Hibernate在缓存中无法找到Group对象  
  26.             //揭露:Persistent状态的对象不能引用Transient状态的对象  
  27.             //该问题在testSave2方法中更改  
  28.             session.getTransaction().commit();  
  29.         }catch(Exception e){  
  30.               
  31.             e.printStackTrace();  
  32.             session.getTransaction().rollback();  
  33.         }finally{  
  34.             GetSession.CloseSession(session);  
  35.         }  
  36.     }  

        但是使用上面的代码在执行写入时会报错TransientObjectException,这是因为在保存User对象时它会按照<many-to-one>中添加的group去内存中查找group对象,但是上面的代码中group对象一直都是在Transient状态中,并没有被session管理,也就是说查找不到session对象,而User对象进入了Persistent状态,于是会报此错误。正确的代码如下:

  1. public void testSave2(){  
  2.               
  3.     Session session=null;  
  4.     try{  
  5.         session=GetSession.getSession();  
  6.         session.beginTransaction();  
  7.           
  8.         Group group=new Group();  
  9.         group.SetName("动力节点");  
  10.         session.save(group);       //此处将group对象设置为Persistent对象  
  11.           
  12.         User user1=new User();  
  13.         user1.SetName("张三");  
  14.         user1.SetGroup(group);  
  15.           
  16.         User user2=new User();  
  17.         user2.SetName("李四");  
  18.         user2.SetGroup(group);  
  19.           
  20.         session.save(user1);  
  21.         session.save(user2);  
  22.           
  23.         //可以正确的保存数据  
  24.         //因为Group和User都是Persistent状态的对象  
  25.         //所以在Hibernate清理缓存时在session中可以找到关联对象  
  26.         session.getTransaction().commit();  
  27.     }catch(Exception e){  
  28.           
  29.         e.printStackTrace();  
  30.         session.getTransaction().rollback();  
  31.     }finally{  
  32.         GetSession.CloseSession(session);  
  33.     }  
  34. }  

  2、级联映射


         除了上面所说的将group对象和user对象都转化到Persistent对象外,还可以使用cascade级联映射属性,在<many-to-one>属性中添加cascade属性,并复制为save-update,在group对象并非为Persistent状态时即可写入数据库。这样只需要将两个user对象的Group属性设置为同一个group对象即可实现多对一的映射关系,此时User.hbm.xml中对应的内容为如下代码:


  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  4. <!-- Generated 2014-5-14 23:39:25 by Hibernate Tools 3.4.0.CR1 -->  
  5. <hibernate-mapping>  
  6.     <class name="com.hibernate.User" table="USER">  
  7.         <id name="id" type="java.lang.Long">  
  8.             <column name="ID" />  
  9.             <generator class="assigned" />  
  10.         </id>  
  11.           
  12.         <!-- 级联修改表 -->  
  13.         <many-to-one name="group" column="groupid" cascade="save-update"></many-to-one>  
  14.           
  15.     </class>  
  16. </hibernate-mapping>  

       Note:cascade设置为save-update后即可实现向数据库中级联修改、添加和删除,但是具体的级联查询操作却不可以。
       对应的测试配置文件的方法为如下代码:
  1. //级联cascade  
  2. public void testSave3(){  
  3.       
  4.     Session session=null;  
  5.     try{  
  6.         session=GetSession.getSession();  
  7.         session.beginTransaction();  
  8.           
  9.         Group group=new Group();  
  10.         group.SetName("动力节点");  
  11.           
  12.         User user1=new User();  
  13.         user1.SetName("张三");  
  14.         user1.SetGroup(group);  
  15.           
  16.         User user2=new User();  
  17.         user2.SetName("李四");  
  18.         user2.SetGroup(group);  
  19.           
  20.         session.save(user1);  
  21.         session.save(user2);  
  22.           
  23.         //没有抛出TransientObjectException异常  
  24.         //因为使用了级联  
  25.         //Hibernate会首先保存User的关联对象Group  
  26.         //Group和User就都是Persistent状态的对象了  
  27.         session.getTransaction().commit();  
  28.     }catch(Exception e){  
  29.         e.printStackTrace();  
  30.         session.getTransaction().rollback();  
  31.     }finally{  
  32.         GetSession.CloseSession(session);  
  33.     }  
  34. }  



  3、对比升华


        两种方法同样实现了多对一的映射方法,结果上是相同的,但在实现上很不相同。无论是第一种还是第二种采用的都是<many-to-one>在many一端的映射文件中添加该标签,并将标签的name属性赋值为该映射文件注册的类中的one一端的属性值,这样就完成了多对一的基本映射,这是相同点。不同点是直接映射关系没有采用Hibernate字段的属性,这样在实现上较灵活,不但支持增删改,而且可以查询;第二种的cascade级联修改则采用了Hibernate提供的方法,此种方法只支持增删改,并不支持查询。

结语

        

       文章介绍了两种方法来实现多对一的映射,这两种方法在实现结果上是相同的,都是采用的<many-to-one>多对一标签,实现上很简单。需要注意的是第一种方法必须将组对象和用户全部转化为Transient状态,都必须被Session管理这样在保存时才能够在Session中查找到两种对象。多对一映射是经常使用的,另外还有其它的映射关系,将会在下篇文章中讨论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值