Hibernate關聯關係

333111444發表於2008-03-02

http://blog.csdn.net/wmingluo/archive/2006/09/05/1180972.aspx

在域模型鍾,類與類之間最普遍的關係就是關聯關係。例如,客戶(Customer)和訂單(Order)的關係,一個客戶能發出多個訂單,耳一個訂單隻能屬於一個客戶。從OrderCustomer的關係是多對一關係,意味著每個Order象都會引用一個Customer物件,下面就以CustomerOrder之間的關係例子來介紹關聯關係。

一, 一對多的單向關聯關係

[@more@]

首先先建立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"

">

<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"

">

<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"

">

<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

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12058779/viewspace-1000307/,如需轉載,請註明出處,否則將追究法律責任。

相關文章