hibernate的映射可以說是hibernate中最複雜的部分了。我們一步一步來,首先說一對多關聯關係,我們知道在數據庫只能用外鍵而且只能出表示一對多和多對一的單向關係,而在hibernate中還有另外一種就是一對多雙向關聯。
Order到Customer的多對一單項關聯
Customer到Order的一對多單項關聯
Customer和Order的一對多雙向關聯
1.多對一的單向關聯關係
第一次我把整個映射文件列出來,後面我就只列關鍵部分了
Order.hbm.xml文件配置如下:
- <?xml version="1.0"?>
- <!DOCTYPE hibernate-mapping
- PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.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"
- lazy="false"
- />
- </class>
- </hibernate-mapping>
BusinessService.java的代碼如下:
- package mypack;
- import org.hibernate.*;
- import org.hibernate.cfg.Configuration;
- import java.util.*;
- public class BusinessService{
- public static SessionFactory sessionFactory;
- static{
- try{
- /* 初始化hibernate,建立SessionFactory實例*/
- Configuration config = new Configuration();
- config.configure();
- sessionFactory = config.buildSessionFactory();
- }catch(RuntimeException e){e.printStackTrace();throw e;}
- }
- /* 查詢customer對象關聯的所有order對象*/
- public List findOrdersByCustomer(Customer customer){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- List orders=session.createQuery("from Order as o where o.customer.id="+customer.getId())
- .list();
- tx.commit();
- return orders;
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- throw e;
- } finally {
- session.close();
- }
- }
- /* 按照OID查詢customer對象*/
- public Customer findCustomer(long customer_id){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- Customer customer=(Customer)session.get(Customer.class,new Long(customer_id));
- tx.commit();
- return customer;
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- throw e;
- } finally {
- session.close();
- }
- }
- /*級聯保存customer和order對象,該方法的前提是在Order.hbm.xml中的<many-to-one> 標籤中配置casecade="save-update",
- * 當保存Order時,會自動持久化處於臨時狀態的customer對象,保存到數據庫時會級聯保存那個“one”,也即是customer對象*/
- public void saveCustomerAndOrderWithCascade(){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- Customer customer=new Customer("Jack");
- Order order1=new Order("Jack_Order001",customer);
- Order order2=new Order("Jack_Order002",customer);
- session.save(order1);
- session.save(order2);
- tx.commit();
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- e.printStackTrace();
- } finally {
- session.close();
- }
- }
- /* 分別保存Customer和Order對象(主動持久化Customer),
- * 也可以達到saveCustomerAndOrderWithCascade的效果,而且不用配置casecade="save-update",
- *關於這個在實際應用中哪一個比較好還要看實際要求,就客戶和訂單這方面來說,肯定要配置casecade比較好,因爲不可能存在沒有客戶的訂單吧!*/
- public void saveCustomerAndOrder(){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- Customer customer=new Customer("Tom");
- session.save(customer);
- Order order1=new Order("Tom_Order001",customer);
- Order order2=new Order("Tom_Order002",customer);
- session.save(order1);
- session.save(order2);
- tx.commit();
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- throw e;
- } finally {
- session.close();
- }
- }
- /*打印訂單*/
- public void printOrders(List orders){
- for (Iterator it = orders.iterator(); it.hasNext();) {
- Order order=(Order)it.next();
- System.out.println("OrderNumber of "+order.getCustomer().getName()+ " :"+order.getOrderNumber());
- }
- }
- public void test(){
- // saveCustomerAndOrder();
- saveCustomerAndOrderWithCascade();
- System.out.println("\n\n****************打印客戶號爲1的所有Order********************");
- Customer customer=findCustomer(1);
- List orders=findOrdersByCustomer(customer);
- printOrders(orders);
- }
- public static void main(String args[]){
- new BusinessService().test();
- sessionFactory.close();
- }
- }
customer的配置我就不寫了,注意上面的配置order.hbm,xml中的not-null=”true”表明該字段是不允許爲空的,也就是插入一個order的時候必須指定其customer的ID。
本文由上海java培訓機構官網推薦閱讀,更多精彩請參照上海it培訓官網。
【運行結果】 :
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into CUSTOMERS (NAME, ID) values (?, ?)
Hibernate: insert into ORDERS (ORDER_NUMBER, CUSTOMER_ID, ID) values (?, ?, ?)
Hibernate: insert into ORDERS (ORDER_NUMBER, CUSTOMER_ID, ID) values (?, ?, ?)
****************打印客戶號爲1的所有Order********************
Hibernate: select customer0_.ID as ID0_, customer0_.NAME as NAME0_0_ from CUSTOMERS custom
er0_ where customer0_.ID=?
Hibernate: select order0_.ID as ID, order0_.ORDER_NUMBER as ORDER2_1_, order0_.CUSTOMER_ID
as CUSTOMER3_1_ from ORDERS order0_ where order0_.CUSTOMER_ID=1
Hibernate: select customer0_.ID as ID0_, customer0_.NAME as NAME0_0_ from CUSTOMERS custom
er0_ where customer0_.ID=?
OrderNumber of Tom :Tom_Order001
OrderNumber of Tom :Tom_Order002
2.一對多的雙向關聯關係
上面的Order.hbm,xml配置不變,所以我們擁有從order到Customer的單向關聯,如果要查詢某個訂單的Customer,我們直接使用order.getCustomers()就可以了,那我們怎麼才能擁有從Customer到order的關聯呢?當這裏兩個關聯都有了,那麼就一對多的雙向關聯,我們要實現的需求就是直接customer.getOrders()就取出這個客戶的所有訂單了。當然這其中還有很多實際應用方面的另外要求,比如作爲一個商場不可能客戶以來就把他所有的訂單都取出來吧,這其中我們要怎麼控制,怎麼配置我們也會說到。
在Customer.java中要增加如下聲明並提供其set和get方法:
private Set orders = new HashSet();
根據上面給出Customer.hbm.xml的配置:
- <?xml version="1.0"?>
- <!DOCTYPE hibernate-mapping
- PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.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>
2.1配置介紹:上面配置了cascade="save-update" 表明當保存或更新Customer對象時,會級聯保存或更新orders集合中的所有order對象,如下面的方法:
- public void saveCustomerAndOrderWithCascade(){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- //創建customer和order對象
- Customer customer=new Customer("Tom",new HashSet());
- Order order=new Order();
- order.setOrderNumber("Tom_Order001");
- //建立customer和order的關聯關係
- order.setCustomer(customer);
- customer.getOrders().add(order);//將order加入到customer的set中
- // 保存Customer對象,hibernate會級聯保存orders
- session.save(customer);
- tx.commit();
- idOfTom=customer.getId();
- idOfTomOrder=order.getId();
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- e.printStackTrace();
- } finally {
- session.close();
- }
- }
該方法的【運行結果】是:
Hibernate: insert into CUSTOMERS (NAME, ID) values (?, ?)
Hibernate: insert into ORDERS (ORDER_NUMBER, CUSTOMER_ID, ID) values (?, ?, ?)
2.2介紹<set>元素的inverse屬性
我們首先假設數據庫中有兩個沒有關聯的Custermer和Order,我們要將其取出,並建立關聯關係。其方法如下:
- public void associateCustomerAndOrder(){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- Customer customer=(Customer)session.load(Customer.class,idOfJack);
- Order order=(Order)session.load(Order.class,idOfJackOrder);
- order.setCustomer(customer); //建立custermer到order的關聯
- customer.getOrders().add(order);//將order加入到custermer的set中
- tx.commit(); //根據持久化對象的狀態執行相應sql語句
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- e.printStackTrace();
- } finally {
- session.close();
- }
- }
上面方法運行後的【結果】是:
update ORDERS set ORDER_NUMBER = ‘JACK_Oredr001’,CUSTOMER_ID=2 where ID=2
update ORDERS set CUSTOMER_ID=2 where ID=2
我們在數據庫中明明只修改了一條記錄,爲什麼會有兩個sql語句呢,原來是因爲hibernate會安裝持久化對象的屬性變化來同步更新數據庫,
當執行到
時,hibernate檢測到order的變化則會計劃執行相應如下的sql語句:
當執行到
時,hibernate有檢測到customer有上述變化了,於是執行如下sql語句:
顯然,這一步是完全沒有必要的,爲提高hibernate性能,防止執行多餘的sql語句,我們可以在Customer.hbm.xml中的<set>元素中加入如下配置inverse="true",這段代碼表明Customer關聯的知識Order端的鏡像,當hibernate探測到持久化對象Customer和Order都發生變化時,只安裝Order的變化來同步更新數據庫。
其實:我們上面的代碼中如果註釋掉這段代碼,
order.setCustomer(customer);//建立custermer到order的關聯
我們即使不是設置inverse屬性,程序也只執行
這一段sql語句,
Tip:但是我們爲了程序的健壯性,在兩個對象有雙向關聯關係的時候,我們應該同時修改兩端的對象的響應屬性,爲避免執行多餘的sql語句,我們還應該在set中設置inverse屬性,亦然我們在解除雙向關聯的關係時,也應該修改兩端的屬性,執行如下代碼
2.3級聯刪除
考慮我們在刪除某個Customer的時候,肯定也要刪除對應order的記錄,我們就應該設置cascade="delete"
考慮我們在解除某個Customer和某Order的關聯關係的時候,如果要刪除該order的記錄,同時我們還要保證其“save-update“,和”delete”的功能,我們就可以設置cascade="all-delete-orphan"(orphan是“孤兒”的意思)
關於casecade的配置詳細表如下:
<!--[endif]-->
2.一對多的雙向自身關聯關係
<!--[endif]-->
自身關聯
該類Category.hbm.xml的配置如下:
- <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>
hibernate的映射到此先告以段落,但是你如果以爲hibernate的映射到此就結束了就大錯特錯了,hibernate的映射遠不止這一點。但是上面的是平時比較常用的方式。關於映射我們後面會繼續深入來討論。下一節先打個基礎說說hibernate是如何操縱對象的。