Hibernate关联关系

   在域模型钟,类与类之间最普遍的关系就是关联关系。例如,客户(Customer)和订单(Order)的关系,一个客户能发出多个订单,耳一个订单只能属于一个客户。从OrderCustomer的关系是多对一关系,意味着每个Order象都会引用一个Customer对象,下面就以CustomerOrder之间的关系例子来介绍关联关系。

 

一, 一对多的单向关联关系

 

首先先建立CustomerOrder类,代码如下:

 

Customer

 

package mypack;

import java.io.Serializable;

import org.apache.commons.lang.builder.ToStringBuilder;

 

public class Customer implements Serializable {

 

    private Long id;

 

    private String name;

 

    public Customer(String name) {

        this.name = name;

    }

 

    public Customer() {

    }

 

    public Long getId() {

        return this.id;

    }

 

    public void setId(Long id) {

        this.id = id;

    }

 

    public String getName() {

        return this.name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    public String toString() {

        return new ToStringBuilder(this)

            .append("id", getId())

            .toString();

    }

 

}

 

 

Order

 

package mypack;

import java.io.Serializable;

import org.apache.commons.lang.builder.ToStringBuilder;

 

public class Order implements Serializable {

 

    private Long id;

 

    private String orderNumber;

 

    private mypack.Customer customer;

 

    public Order(String orderNumber, mypack.Customer customer) {

        this.orderNumber = orderNumber;

        this.customer = customer;

    }

 

    public Order() {

    }

 

    public Order(mypack.Customer customer) {

        this.customer = customer;

    }

 

    public Long getId() {

        return this.id;

    }

 

    public void setId(Long id) {

        this.id = id;

    }

 

    public String getOrderNumber() {

        return this.orderNumber;

    }

 

    public void setOrderNumber(String orderNumber) {

        this.orderNumber = orderNumber;

    }

 

    public mypack.Customer getCustomer() {

        return this.customer;

    }

 

    public void setCustomer(mypack.Customer customer) {

        this.customer = customer;

    }

 

    public String toString() {

        return new ToStringBuilder(this)

            .append("id", getId())

            .toString();

    }

 

}

 

 

CustomerOrder类的配置文件分别是Customer.hbm.xmlOrder.hbm.xml

 

Customer.hbm.xml如下:

 

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping

PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping >

 

  <class name="mypack.Customer" table="CUSTOMERS" >

    <id name="id" type="long" column="ID">

      <generator class="increment"/>

    </id>

 

    <property name="name" type="string" >

        <column name="NAME" length="15" />

    </property>

     

  </class>

 

</hibernate-mapping>

 

 

Order.hbm.xml如下:

 

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping

PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping >

 

   <class name="mypack.Order" table="ORDERS">

    

      <id name="id" type="long" column="ID">

        <generator class="increment"/>

      </id>

  

      <property name="orderNumber" type="string" >

        <column name="ORDER_NUMBER" length="15" />

      </property>

 

     <many-to-one

        name="customer"

        column="CUSTOMER_ID"

        class="mypack.Customer"

        not-null="true"

     />

 

    </class>

 

</hibernate-mapping>

 

Order.hbm.xml<many-to-one>元素建立了CustomerORDERS表的外键CUSTOMER_ID之间

的映射。

它包括如下属性。

 

  name:设定待映射的持久化类的属性名,此处为Order类的customer属性。

column:设定和持久化类的属性对应的表的外键,此处为ORDERS表的外键CUSTOMER_ID

  class:设定持久化类的属性的类型,此处设定customer属性为Customer类。

  notnull:如果为ture表示customer属性不能为null,默认为false

 

接下来我们持久化一个Customer对象以及和他关联的Order对象:

(以下代码并非是完整代码,只是简单把逻辑列出)

     

       Configuration config = new Configuration();

                         config.addClass(Customer.class)

             .       addClass(Order.class);

       sessionFactory = config.buildSessionFactory();

       Session session = sessionFactory.openSession();

       Transaction tx = null;

       tx = session.beginTransaction();

       Customer customer=new Customer("Jack");

       session.save(customer);

       Order order1=new Order("Jack_Order001",customer);

       Order order2=new Order("Jack_Order002",customer);

       session.save(order1);

       session.save(order2);

       tx.commit();

 

当我们要持久化一个Customer对象以及和他关联的Order对象的时候一定要先建立Customer象并持久化它,否则当创建Order持久对象的时候会抛出异常,如果把session.save(customer)

注销将会在session.save(order1)抛出PropertyValueException异常。分析产生异常原因:在调用session.save(order1)方法之前,order1customer对象都是临时对象,临时对象就是刚刚用new创建出来,但是还没有持久会的对象,而Hibernate不会自动持久化order1关联的customer对象,在数据库中意味着仅仅向ORDERS表中插入了一条记录,并且该记录的CUSTOMER_ID字段为null,违反了数据库完整性约束,因为不允许ORDERS表的CUSTOMER_ID字段为null

 

从上面的例子可以看出当Hibernate持久化一个临时对象时,在默认情况下,它不会自动持久化所关联的其他临时对象,所以会抛出PropertyValueException异常。如果希望当Hibernate持久化Order对象时自动持久化所关联的Customer对象,可以把<many-to-one>cascade属性设为“saveupdate”cascade属性默认为“none”

                    

             <many-to-one

          name="customer"    

           column="CUSTOMER_ID"

          class="mypack.Customer"

          cascade“saveupdate”

           not-null="true"

                />

 

这样就能够在Hibernate持久化Order对象时自动持久化所关联的Customer对象了

 

 

二, 一对多的双向关联关系

 

我们依然使用前面的CustomerOrder的例子,由于是双向关联,Order类代码如上面一样,由于Customer类中有Order的关联对象,因此Customer类改为如下

 

package mypack;

 

import java.io.Serializable;

import java.util.Set;

import org.apache.commons.lang.builder.ToStringBuilder;

 

public class Customer implements Serializable {

 

    private Long id;

 

    private String name;

 

    private Set orders;

 

    public Customer(String name, Set orders) {

        this.name = name;

        this.orders = orders;

    }

 

    public Customer() {

    }

 

    public Customer(Set orders) {

        this.orders = orders;

    }

 

    public Long getId() {

        return this.id;

    }

 

    public void setId(Long id) {

        this.id = id;

    }

 

    public String getName() {

        return this.name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    public Set getOrders() {

        return this.orders;

    }

 

    public void setOrders(Set orders) {

        this.orders = orders;

    }

 

    public String toString() {

        return new ToStringBuilder(this)

            .append("id", getId())

            .toString();

    }

 

}

 

如何在映射文件中映射集合类型的order属性呢?由于在CUSTOMERS表中没有直接与order属性对应的字段,因此不能用<property>元素来映射order属性,而是要使用<set>元素:

 

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping

PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping >

 

  <class name="mypack.Customer" table="CUSTOMERS" >

    <id name="id" type="long" column="ID">

      <generator class="increment"/>

    </id>

 

    <property name="name" type="string" >

        <column name="NAME" length="15" />

    </property>

 

    <set

        name="orders"

        cascade="save-update"

      

        >

       

        <key column="CUSTOMER_ID" />

        <one-to-many class="mypack.Order" />

     </set>  

  </class>

</hibernate-mapping>

 

<set>元素还包含两个子元素:<key><one-to-many><one-to-many>元素设定所关联的持久化类,此处为Order类,<key>元素设定与所关联的持久化对应的表的外键,此处为ORDERS表的CUSTOMERS_ID字段。

它包括如下属性:

  <set>元素表明Customer类的orders属性为java.util.Set集合类型。

  <one-to-many>表明orders集合中存放的是一组Order对象。

  <key>属性表明ORDERS表通过外键CUSTOMERS_ID参照CUSTOMERS表。

  cascade 属性取值为"save-update",表明当保存或更新Customer对象时,会级联保存或更新orders集合中的所有Order对象。  

 

接下来我们持久化一个Customer对象以及和他关联的Order对象:

 

              Session session = sessionFactory.openSession();

          Transaction tx = null;

           tx = session.beginTransaction();

            Customer customer=new Customer("Tom",new HashSet());

          Order order=new Order();

          order.setOrderNumber("Tom_Order001");

            order.setCustomer(customer);

          customer.getOrders().add(order);

            session.save(customer);

          tx.commit();

 

<set>元素的cascade属性为"save-update"时,Hibernate在持久化Customer对象时,会自动持久化关联的Order对象。

 

如果是加载持久化对象,然后再建立关联关系,那又该怎么做呢?如下所示:

 

Session session = sessionFactory.openSession();

Transaction tx = null;

tx = session.beginTransaction();

Customer customer=(Customer)session.load(Customer.class,new Long(2));

Order order=(Order)session.load(Order.class,new Long(2));

order.setCustomer(customer);

customer.getOrders().add(order);

tx.commit();

 

Hibernate会自动清理缓存中的所有持久化对象,按照持久化对象状态改变来同步更新数据库,Hibernate在清理以上Customer对象和Order对象时执行了以下两条SQL语句:

     update ORDERS set ORDER_NUMBER='Jack_Order001',CUSTOMER_ID=2 where ID=2

       update ORDERS set CUSTOMER_ID=2 where ID=2

尽管只是修改了ORDERS表的一条记录,但是以上SQL语句表明Hibernate执行了两次update操作这是因为HIbernate根据内存中持久化对象的状态变化来决定需要执行的SQL语句,order.setCustomer(customer)执行的相应SQL语句为:

update ORDERS set ORDER_NUMBER='Jack_Order001',CUSTOMER_ID=2 where ID=2

customer.getOrders().add(order)执行的相应SQL语句为:

update ORDERS set CUSTOMER_ID=2 where ID=2

重复执行对于的SQL语句会影响应用程序的性能,那么解决的办法是把<set>元素的inverse属性设置为true,默认为false

 

 <set

        name="orders"

        cascade="save-update"

          inverse"ture"

        >

       

        <key column="CUSTOMER_ID" />

        <one-to-many class="mypack.Order" />

     </set>

因此在映射一对多双向关联关系时,应该在"one"方把inverse属性设为“ture”,这样可以提高性能。

 

三,一对多双向自身关联关系

 

以食品为例,它代表商品类别,存在一对多双向自身关联关系。如下图所示,水果类别属于食品类别,同时它又包含两个子类别:苹果类别和桔子类别。

 

图中每一种商品代表一个Category对象,这些对象形成了树型数据结构。每个Category对象可以和一个父类别Category对象关联,同时也可以和一组子类别Category对象关联。为了表达这种一对多双向自身关联的关系,可以在Category类中定义两个属性:

● parentCategory:引用父类别的Category对象。

● childCategories:引用一组子类别的Category对象。

 

Category类的源代码如下

 

 

package mypack;

 

import java.io.Serializable;

import java.util.Set;

import org.apache.commons.lang.builder.ToStringBuilder;

 

public class Category implements Serializable {

 

    private Long id;

 

    private String name;

 

    private mypack.Category parentCategory;

 

    private Set childCategories;

 

public Category(String name, mypack.Category parentCategory, Set childCategories)

{

        this.name = name;

        this.parentCategory = parentCategory;

        this.childCategories = childCategories;

    }

 

    public Category() {

    }

 

    public Category(Set childCategories) {

        this.childCategories = childCategories;

    }

 

    public Long getId() {

        return this.id;

    }

 

    public void setId(Long id) {

        this.id = id;

    }

 

    public String getName() {

        return this.name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    public mypack.Category getParentCategory() {

        return this.parentCategory;

    }

 

    public void setParentCategory(mypack.Category parentCategory) {

        this.parentCategory = parentCategory;

    }

 

    public Set getChildCategories() {

        return this.childCategories;

    }

 

    public void setChildCategories(Set childCategories) {

        this.childCategories = childCategories;

    }

 

    public String toString() {

        return new ToStringBuilder(this)

            .append("id", getId())

            .toString();

    }

 

}

 

Category.hbm.xml文件如下:

 

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping

PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping >

 

  <class name="mypack.Category" table="CATEGORIES" >

    <id name="id" type="long" column="ID">

      <generator class="increment"/>

    </id>

 

    <property name="name" type="string" >

        <column name="NAME" length="15" />

    </property>

 

    <set

        name="childCategories"

        cascade="save-update"

        inverse="true"

        >

        <key column="CATEGORY_ID" />

        <one-to-many class="mypack.Category" />

     </set>  

 

   <many-to-one

        name="parentCategory"

        column="CATEGORY_ID"

        class="mypack.Category"

       />

 

  </class>

 

</hibernate-mapping>

 

下面我们来演示当<set>元素的cascade属性为“saveupdate”时Hibernate的运行行为,看看如何建立Category对象之间的关系。

 

Session session = sessionFactory.openSession();

    Transaction tx = null;

    tx = session.beginTransaction();

    Category  foodCategory=new Category("food",null,new HashSet());

    Category  fruitCategory=new Category("fruit",null,new HashSet());

    Category  vegetableCategory=new Category

("vegetable",null,new HashSet());

    Category  appleCategory=new Category("apple",null,new HashSet());

    Category  orangeCategory=new Category("orange",null,new HashSet());

    Category tomatoCategory=new Category("tomato",null,new HashSet());

 

      //建立食品类别和水果类别之间的关联关系

      foodCategory.getChildCategories().add(fruitCategory);

      fruitCategory.setParentCategory(foodCategory);

 

      //建立食品类别和蔬菜类别之间的关联关系

      foodCategory.getChildCategories().add(vegetableCategory);

      vegetableCategory.setParentCategory(foodCategory);

 

      //建立水果类别和苹果类别之间的关联关系

      fruitCategory.getChildCategories().add(appleCategory);

      appleCategory.setParentCategory(fruitCategory);

 

      //建立水果类别和桔子类别之间的关联关系

      fruitCategory.getChildCategories().add(orangeCategory);

      orangeCategory.setParentCategory(fruitCategory);

 

      //建立西红柿类别和水果类别之间的关联关系

      tomatoCategory.setParentCategory(fruitCategory);

      fruitCategory.getChildCategories().add(tomatoCategory);

 

      session.save(foodCategory);

      tx.commit();

由于<set>元素的cascade属性为“saveupdate”,所以当Hibernate在持久化一个Category对象时,会自动持久化关联的其他Category对象。

 

 

参考书籍:《精通Hibernate》

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值