以Customer和Order爲例:
一對多:
每個Customer可以有一個或者多個Order,因此Customer中應該有一個集合類型的屬性,來引用所有關聯的Order對象。
多對一:
Order到Customer的關聯爲多對一關聯,每個Order對象都要有一個Customer對象。在Order中要有一個Customer類型的屬性。
如果僅包含Customer到Order或者僅包含Order呆Customer的關聯,就叫做單向關聯。
同時包含兩個關聯的就坐雙向關聯。
第一部分:以Order和Customer爲例,介紹如何映射多對一單向關聯關係。
這個例子的特點是隻在Order中加入Customer屬性,建立單向關聯。
1 數據庫:
alter table ORDERS drop foreign key FK8B7256E516B4891C;
drop table if exists CUSTOMERS;
drop table if exists ORDERS;
create table CUSTOMERS (
ID bigint not null,
NAME varchar(15),
primary key (ID)
);
create table ORDERS (
ID bigint not null,
ORDER_NUMBER varchar(15),
CUSTOMER_ID bigint not null,
primary key (ID)
);
alter table ORDERS add index FK8B7256E516B4891C (CUSTOMER_ID), add constraint FK8B7256E516B4891C foreign key (CUSTOMER_ID) references CUSTOMERS (ID);
2 類
Customers.java
package mypack;
import java.util.HashSet;
import java.util.Set;
/**
*CustomersgeneratedbyMyEclipsePersistenceTools
*/
publicclass Customers implements java.io.Serializable {
// Fields
private Long id;
private String name;
// Constructors
/**defaultconstructor*/
public Customers() {
}
/**fullconstructor*/
public Customers(String name) {
this.name = name;
}
// Property accessors
public Long getId() {
returnthis.id;
}
publicvoid setId(Long id) {
this.id = id;
}
public String getName() {
returnthis.name;
}
publicvoid setName(String name) {
this.name = name;
}
}
Orders.java
package mypack;
/**
*OrdersgeneratedbyMyEclipsePersistenceTools
*/
publicclass Orders implements java.io.Serializable {
// Fields
private Long id;
private Customers customers;
private String orderNumber;
// Constructors
/**defaultconstructor*/
public Orders() {
}
/**minimalconstructor*/
public Orders(Customers customers) {
this.customers = customers;
}
/**fullconstructor*/
public Orders(Customers customers, String orderNumber) {
this.customers = customers;
this.orderNumber = orderNumber;
}
// Property accessors
public Long getId() {
returnthis.id;
}
publicvoid setId(Long id) {
this.id = id;
}
public Customers getCustomers() {
returnthis.customers;
}
publicvoid setCustomers(Customers customers) {
this.customers = customers;
}
public String getOrderNumber() {
returnthis.orderNumber;
}
publicvoid setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
}
測試類:
BusinessService.java
package mypack;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.sun.org.apache.xpath.internal.operations.Or;
import sessionFactory.HibernateSessionFactory;
publicclass BusinessService {
public List findOrdersByCustomer(Customers customer) throws Exception {
Session session = null;
Transaction tr = null;
try {
session = HibernateSessionFactory.getSession();
tr = session.beginTransaction();
Query query = session
.createQuery("from Orders as o where o.customers.id="
+ customer.getId());
List orders = query.list();
tr.commit();
return orders;
} catch (Exception e) {
// TODO: handle exception
if (tr != null) {
tr.rollback();
}
throw e;
} finally {
session.close();
}
}
public Customers findCustomer(Long customer_id) throws Exception {
Session session = null;
Transaction tr = null;
try {
session = HibernateSessionFactory.getSession();
tr = session.beginTransaction();
Customers customer = (Customers) session.load(Customers.class,
customer_id);
tr.commit();
return customer;
} catch (Exception e) {
// TODO: handle exception
if (tr != null) {
tr.rollback();
}
throw e;
} finally {
session.close();
}
}
publicvoid saveCustomerAndOrder() throws Exception {
Session session = null;
Transaction tr = null;
try {
session = HibernateSessionFactory.getSession();
tr = session.beginTransaction();
Customers customer = new Customers("zhang");
session.save(customer);
Orders order1 = new Orders(customer, "order1");
Orders order2 = new Orders(customer, "order2");
session.save(order1);
session.save(order2);
tr.commit();
} catch (Exception e) {
// TODO: handle exception
if (tr != null) {
tr.rollback();
}
throw e;
} finally {
session.close();
}
}
publicvoid saveCustomerAndOrdeWithCascader() throws Exception {
Session session = null;
Transaction tr = null;
try {
session = HibernateSessionFactory.getSession();
tr = session.beginTransaction();
Customers customer = new Customers("zhang");
// session.save(customer);//映射文件中加入cascade="save-update"
Orders order1 = new Orders(customer, "order1");
Orders order2 = new Orders(customer, "order2");
session.save(order1);
session.save(order2);
tr.commit();
} catch (Exception e) {
// TODO: handle exception
if (tr != null) {
tr.rollback();
}
throw e;
} finally {
session.close();
}
}
publicvoid printOrders(List orders) {
for (Iterator it = orders.iterator(); it.hasNext();) {
Orders order = (Orders) it.next();
System.out.println("OrderNumber of "
+ order.getCustomers().getName() + " :"
+ order.getOrderNumber());
}
}
publicvoid test() throws Exception {
saveCustomerAndOrder();
saveCustomerAndOrdeWithCascader();
Customers customer = findCustomer(new Long(1));
List orders = findOrdersByCustomer(customer);
printOrders(orders);
}
/**
*@paramargs
*@throwsException
*/
publicstaticvoid main(String[] args) throws Exception {
// TODO Auto-generated method stub
BusinessService b = new BusinessService();
b.test();
}
}
3 映射文件:
Customers.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="mypack.Customers" table="customers"
catalog="onetomany" lazy="false">
<id name="id" type="java.lang.Long">
<column name="ID" />
<generator class="increment" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" length="15" />
</property>
</class>
</hibernate-mapping>
Orders.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="mypack.Orders" table="orders" catalog="onetomany">
<id name="id" type="java.lang.Long">
<column name="ID" />
<generator class="increment" />
</id>
<many-to-one name="customers" class="mypack.Customers"
fetch="select" cascade="save-update">
<column name="CUSTOMER_ID" not-null="true" />
</many-to-one>
<property name="orderNumber" type="java.lang.String">
<column name="ORDER_NUMBER" length="15" />
</property>
</class>
</hibernate-mapping>
小結:本例子的重點是級聯保存和更新的使用。當建立了Order到Customer的單向關聯時,把映射文件中的<many-to-one>的cascade屬性設置爲save-update,只做保存Order的操作,與Order對應的customer對象就自動被保存了。(前提是在測試的文件中加入了對Order的Customer屬性的設置)
映射一對多雙向關聯關係
1 Customers 類中加如set類型的 Orders屬性,Orders類中加入Customer類型的customer屬性。
2 測試時,建立兩者一對多的雙向關聯關係。
Customers customer=new Customer(“Tom”,new HashSet());
Order order=new Order();
建立Customer對象和Orders對象的雙向關聯關係。
order.setCustomer(customer);
customer.getOrders().add(order);
只要將 Set的cascade屬性設置爲save-update,就可以直接保存customer,與之關聯的order對象就會自動被持久化。
Session.save(customer);
3 set的inverse屬性。
當建立了對象的雙向關聯關係時,Hibernate會根據:
order.setCustomer(customer);
customer.getOrders().add(order);
對數據庫做兩側更新,其實這裏是重複執行了SQL語句,勢必會影響到程序的反應速度和性能。
只要將設置set inverse=”true”,
<set name="orderses" inverse="true" cascade="save-update">
<key>
<column name="CUSTOMER_ID" not-null="true" />
</key>
<one-to-many class="mypack.Orders" />
</set>
這段代碼表示:在Customer和Order的雙向關聯關係時中,Customer的關聯只是Order關聯的鏡像.當Hibernate測到持久化對象Customer和Order對象的狀態都發生變化時,僅僅按照Order對象的狀態變化同步更新數據庫。
小結:1 在映射一對多的雙向關聯關係時,應該在one方把 inverse屬性設成true,這樣可以提高應用的性能。
2 在建立兩個對象的雙向關聯時,應該同時修改關聯兩端的對象的相應屬性:
order.setCustomer(customer);
customer.getOrders().add(order);
這樣纔會使程序更加健壯,提高業務邏輯的獨立性。
Cascade的delete屬性:如果希望在刪除Customer對象時同時刪除與之關聯的Order對象。
Cascade的all-delete-orphan屬性:
1 保存或更新Customer對象時,級聯保存更新所有關聯的Order對象。相當於:save-update
2 當刪除Customer對象時,級聯刪除所有關聯的Order對象。相當於:delete
3 刪除不在和Customer對象關聯的多有Order對象
總結:One To Many 雙相關聯
Java類中: One加入 Set類型的 Many屬性,Many加入One類型的屬性。
業務程序中:爲了使程序更加健壯,建立One到Many,和Many到one的雙向關聯。
影射文件中:將One中的set 的inverse屬性設成true(默認是false),這樣Hibernate不會因爲我們在業務邏輯中做的雙向關聯而產生重複的更新數據庫的操作,也提高了效率。
適當選擇cascade的值:save-update ,delete , all-delete-orphan
自身一對多雙向關聯關係
1 數據庫:
altertable CATEGORIES dropforeignkey FK6A31321CDBFCB7FC;
droptableifexists CATEGORIES;
createtable CATEGORIES (
ID bigintnotnull,
NAMEvarchar(15),
CATEGORY_ID bigint,
primarykey (ID)
);
altertable CATEGORIES addindex FK6A31321CDBFCB7FC (CATEGORY_ID), addconstraint FK6A31321CDBFCB7FC foreignkey (CATEGORY_ID) references CATEGORIES (ID);
三個字段: id name category_id(外鍵,參照本表的id主鍵)
2 類文件
package mypack;
import java.util.HashSet;
import java.util.Set;
/**
*CategoriesgeneratedbyMyEclipsePersistenceTools
*/
publicclass Categories implements java.io.Serializable {
// Fields
private Long id;
private Categories parentCategory;
private String name;
private Set childCategorieses = new HashSet(0);
// Constructors
/**defaultconstructor*/
public Categories() {
}
public Categories(String name, Categories parentCategory,
Set childCategorieses) {
this.name = name;
this.parentCategory = parentCategory;
this.childCategorieses = childCategorieses;
}
/**fullconstructor*/
// Property accessors
public Long getId() {
returnthis.id;
}
publicvoid setId(Long id) {
this.id = id;
}
public String getName() {
returnthis.name;
}
publicvoid setName(String name) {
this.name = name;
}
public Set getChildCategorieses() {
returnchildCategorieses;
}
publicvoid setChildCategorieses(Set childCategorieses) {
this.childCategorieses = childCategorieses;
}
public Categories getParentCategory() {
returnparentCategory;
}
publicvoid setParentCategory(Categories parentCategory) {
this.parentCategory = parentCategory;
}
}
分析:
private Categories parentCategory;
指向某個分類的父類,父類只能是一個Category類型的對象。
private Set childCategorieses = new HashSet(0);
指向某個分類的子類:子類可以是一個或者多個的Category類型對象。
3映射文件
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="mypack.Categories" table="categories"
catalog="onetomany">
<id name="id" type="java.lang.Long">
<column name="ID" />
<generator class="increment" />
</id>
<many-to-one name="parentCategory" class="mypack.Categories"
fetch="select" cascade="save-update">
<column name="CATEGORY_ID" />
</many-to-one>
<property name="name" type="java.lang.String">
<column name="NAME" length="15" />
</property>
<set name="childCategorieses" inverse="true" lazy="false"
cascade="save-update">
<key>
<column name="CATEGORY_ID" />
</key>
<one-to-many class="mypack.Categories" />
</set>
</class>
</hibernate-mapping>
注意紅色部分的parentCategory childCategorieses 屬性的映射方法。他們都依賴外鍵 <column name="CATEGORY_ID" />
4 測試類
package mypack;
import java.util.HashSet;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import sessionFactory.HibernateSessionFactory;
publicclass BusinessService {
publicvoid saveCategoryWithCascade() throws Exception {
Session session = null;
Transaction tr = null;
try {
session = HibernateSessionFactory.getSession();
tr = session.beginTransaction();
Categories foodCategory = new Categories("food", null,
new HashSet());
Categories fruitCategory = new Categories("fruit", null,
new HashSet());
Categories vegetableCategory = new Categories("vegetable", null,
new HashSet());
Categories appleCategory = new Categories("apple", null,
new HashSet());
Categories orangeCategory = new Categories("orange", null,
new HashSet());
Categories tomatoCategory = new Categories("tomato", null,
new HashSet());
/*
* 建立食品類別和水果類別之間的關聯關係
*/
foodCategory.getChildCategorieses().add(fruitCategory);
fruitCategory.setParentCategory(foodCategory);
/*
* 建立食品類別和蔬菜類別之間的關聯關係
*/
foodCategory.getChildCategorieses().add(vegetableCategory);
vegetableCategory.setParentCategory(foodCategory);
/*
* 建立水果類別和蘋果之間的關聯關係
*/
fruitCategory.getChildCategorieses().add(appleCategory);
appleCategory.setParentCategory(fruitCategory);
/*
* 建立水果類別和橘子之間的關聯關係
*/
fruitCategory.getChildCategorieses().add(orangeCategory);
orangeCategory.setParentCategory(fruitCategory);
/*
* 建立水果類別和番茄之間的關聯關係
*/
fruitCategory.getChildCategorieses().add(tomatoCategory);
tomatoCategory.setParentCategory(fruitCategory);
session.save(foodCategory);
tr.commit();
} catch (Exception e) {
// TODO: handle exception
if (tr != null) {
tr.rollback();
}
throw e;
} finally {
session.close();
}
}
publicvoid modifyCategoryAssociation() throws Exception {
Session session = null;
Transaction tr = null;
try {
session = HibernateSessionFactory.getSession();
tr = session.beginTransaction();
Categories fruitCategory = findCategoryByName(session, "fruit");
Categories tomatoCategory = findCategoryByName(session, "tomato");
Categories vegetableCategory = findCategoryByName(session,
"vegetable");
/*
* 建立蔬菜類別和西紅柿之間的關聯關係
*/
vegetableCategory.getChildCategorieses().add(tomatoCategory);
tomatoCategory.setParentCategory(vegetableCategory);
/*
* 解除西紅柿和水果之間的關係
*/
fruitCategory.getChildCategorieses().remove(tomatoCategory);
tr.commit();
} catch (Exception e) {
// TODO: handle exception
if (tr != null) {
tr.rollback();
}
throw e;
} finally {
session.close();
}
}
public Categories findCategoryByName(Session session, String name)
throws Exception {
Query query = session
.createQuery("from Categories as c where c.name=?");
query.setString(0, name);
Categories category = (Categories) query.uniqueResult();
return category;
}
publicvoid test() throws Exception {
saveCategoryWithCascade();
modifyCategoryAssociation();
}
/**
*@paramargs
*@throwsException
*/
publicstaticvoid main(String[] args) throws Exception {
// TODO Auto-generated method stub
new BusinessService().test();
}
}