城东小巷分享 http://blog.sciencenet.cn/u/chengdong166

博文

映射一对多关联关系

已有 3828 次阅读 2013-9-10 07:05 |个人分类:Java学习|系统分类:科研笔记| Hibernate, 关联映射

        在域模型中,类与类之间最普遍的关系莫过于关联关系。在UML语言中,关联是有方向的,包括单向关系和双向关系。以客户(Customer)和订单(Order)的关系为例,一个客户能发出多个订单,而一个订单只能属于一个客户。

1. 建立多对一的单向关联关系

        本节以订单与客户这种多对一的单向关联关系为例进行说明。

        (1)为建立起多对一的关联关系,Order类需声明对Customer的私有变量以及其访问该变量的setter和getter方法。

        private Customer customer;

        (2)在映射文件中通过<many-to-one>建立customer属性和数据库表orders的外键CUSTOMER_ID之间的映射。

       <many-to-one name="customer"
           column="CUSTOMER_ID"
           class="org.test.pojo.Customers"
           not-null="true"
           lazy="false"
       />

       几点说明:映射文件中的not-null属性默认值为false,若为true则表示customer属性不为null,Hibernate在运行时保存Order对象时会先检查customer属性是否为null;lazy若为false,则Hibernate从数据库中装载Order对象时会自动加载与它关联的Customer对象。
             Transaction trasaction = session.beginTransaction();
             Customers c = new Customers();
             c.setName("chengdongOrder");
             

             Orders order = new Orders();
             order.setCustomer(c);
             order.setOrderNumber("16");
             order.setPrice(10.0);
             session.save(order);
             trasaction.commit();// 自动清理缓存,执行insert操作,提交事务
       运行报错:org.hibernate.PropertyValueException: not-null property references a null or transient value:

org.test.pojo.Orders.customer
       
Hibernate不会自动持久化对象order关联的临时对象c,在数据库中意味着向orders表中新增一条记录并且这条记录的外键CUSTOMER_ID为空,这显然违反了非空约束。因此该异常的根本原因在于order对象的非空属性引用了一个临时变量c。若把not-null="true"去掉,则会报错:

       org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: org.test.pojo.Customers
       这也是由于表orders的外键CUSTOMER_ID不允许为null造成的。解决办法要么在session.save(order)前持久化对象c,例如:

             Transaction trasaction = session.beginTransaction();
             Customers c = new Customers();
             c.setName("chengdongOrder");
             Orders order = new Orders();
             session.save(c);// 持久化对象c,为该对象分配唯一的OID并加入到session缓存
             order.setCustomer(c);
             order.setOrderNumber("17");
             order.setPrice(10.0);
             session.save(order);
             trasaction.commit();// 自动清理缓存,执行insert操作,提交事务

       要么级联保存或更新持久化对象c,cascade="save-update"表明保存或更新当前对象时(即执行update语句时),会级联保存或更新与它关联的对象。

            <many-to-one name="customer"
                        column="CUSTOMER_ID"
                        class="org.test.pojo.Customers"
                        cascade="save-update"
                        not-null="true"
                        lazy="false"
               />

      根据业务需求,有些时候需要将customer与order建立起一对多双向关联关系。

2. 一对多双向关联关系

      order与customer之间的多对一关联关系已建立,下面将建立customer与order之间的一对多的关系。

      (1) Customers类中需声明接口类型的集合属性以及它的setter和getter方法。
            /**
             * Hibernate要求在持久化类声明集合属性时,必须为接口类型.
             * java.util.Set|java.util.Map|java.util.List
             */
            private Set orders = new HashSet();
     (2) 在映射文件中通过<set>映射集合类型的orders属性。
            <set name="orders" cascade="save-update">
                  <!-- key表明ORDERS表通过外键CUSTOMER_ID引用customers表 -->
                  <key column="CUSTOMER_ID" ></key>
                  <!-- 表明orders集合存放的是一组org.test.pojo.Orders类对象 -->
                  <one-to-many class="org.test.pojo.Orders"/>
           </set>
几点说明:
               Transaction trasaction = session.beginTransaction();
 
               Customers c = new Customers();
               c.setName("chengdong1");
 
              Orders order = new Orders();
             order.setOrderNumber("1");
             order.setPrice(1.0);
             // 建立Customers对象与Orders对象一对多双向关联关系
             order.setCustomer(c);
             c.getOrders().add(order);
             // 持久化customer对象的同时持久化与其关联的一些对象
             session.save(c);
             // 清理缓存
             trasaction.commit();

清理缓存时打印出的SQL语句:
   Hibernate: insert into customers (name, ID) values (?, ?)
   Hibernate: insert into orders (ORDER_NUMBER, PRICE, CUSTOMER_ID, ID) values (?, ?, ?, ?)
   Hibernate: update orders set CUSTOMER_ID=? where ID=?

       trasaction.commit()时,Hibernate会自动清理缓存中的所有持久化对象,按照持久化对象的属性变化同步更新到数据库。尽管只是向数据库表中插入数据,但还是执行了update语句。重复执行多余的SQL语句显然会影响Java应用的性能。解决办法是<set>设置inverse="true",即,

       <set name="orders" cascade="save-update" inverse="true">
              <!-- key表明ORDERS表通过外键CUSTOMER_ID引用customers表 -->
              <key column="CUSTOMER_ID" ></key>
              <!-- 表明orders集合存放的是一组org.test.pojo.Orders类对象 -->
              <one-to-many class="org.test.pojo.Orders"/>
     </set>
       这样在建立customer与order之间的双向关联关系时,当Hibernate监测到持久化对象customer和order的属性均发生变化,仅按照order对象属性的变化同步更新数据库。设置inverse="true"后打印的SQL语句:
    Hibernate: insert into customers (name, ID) values (?, ?)
   Hibernate: insert into orders (ORDER_NUMBER, PRICE, CUSTOMER_ID, ID) values (?, ?, ?, ?)


 

 



https://blog.sciencenet.cn/blog-448935-723639.html

上一篇:理解Session缓存
下一篇:Hibernate检索策略
收藏 IP: 122.82.216.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-5-19 01:02

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部