這篇文章主要講解了“Hibernate映射一對多關(guān)聯(lián)關(guān)系是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Hibernate映射一對多關(guān)聯(lián)關(guān)系是什么”吧!
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、成都小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了濂溪免費(fèi)建站歡迎大家使用!
在域模型中,類和類之間最普通的關(guān)系就是關(guān)聯(lián)關(guān)系。在UML語言中,關(guān)聯(lián)是有方向的。以客戶(Customer)和訂單(Order)的關(guān)系為例,一個(gè)客戶可以發(fā)出多個(gè)訂單,而一個(gè)訂單只能屬于一個(gè)客戶。
從Order到Customer的關(guān)聯(lián)是多對一關(guān)聯(lián),這意味著每個(gè)Order對象都會引用一個(gè)Customer對象,因此在Order類中應(yīng)該定義一個(gè)Customer類型的屬性,來引用所關(guān)聯(lián)的Customer對象。
從Customer到Order的關(guān)聯(lián)是一對多的關(guān)聯(lián),這意味著每個(gè)Customer對象都會引用一組Order對象,因此在Customer類中應(yīng)該定義一個(gè)集合類型的屬性,來引用所有關(guān)聯(lián)的Order對象。
一、建立多對一的單向關(guān)聯(lián)關(guān)系
如上例中,我們只需在Order類中定義一個(gè)customer屬性,而在Customer類中無需定義存放Order對象的集合屬性。
Order.java
package mypack; public class Order implements java.io.Serializable { private long id; private String orderNumber; private Customer customer;//定義一個(gè)Customer屬性 public Order() { } public Order(Customer customer) { this.customer = customer; } public Order(String orderNumber, Customer customer) { this.orderNumber = orderNumber; this.customer = customer; } //省略了id,orderNumber的構(gòu)造方法 public Customer getCustomer() { return this.customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
Customer類的所有屬性都是和CUSTOMERS表中的字段一一對應(yīng),因此可以直接使用如下的映射代碼:
<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>
Order類的orderNumber屬性和ORDERS表中ORDER_NUMBER字段對應(yīng),映射代碼和上面類似,此處省去。我們關(guān)注的主要地方是,Order類中的customer屬性,因?yàn)樗荂ustomer類型的,是與ORDERS表的外鍵CUSTOMER_ID對應(yīng)的,它的真實(shí)值是存在CUSTOMERS表中而ORDERS表存的只是對它的引用,因此customer的映射方法不能如上面一樣。
<many-to-one name="customer" column="CUSTOMER_ID" class="mypack.Customer" not-null="true" lazy="false" />
使用方法のBussiness.java演示:
package mypack; import org.hibernate.*; import org.hibernate.cfg.Configuration; import java.util.*; public class BusinessService{ public static SessionFactory sessionFactory; static{ try{ // 初始化 Configuration config = new Configuration(); config.configure(); sessionFactory = config.buildSessionFactory(); }catch(RuntimeException e){e.printStackTrace();throw e;} } /*根據(jù)參數(shù)指定customer的customer_id找出記錄*/ 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(); //Hibernate執(zhí)行:select * from ORDERS where CUSTOMER_ID=customer.getId(); tx.commit(); return orders; }catch (RuntimeException e) { if (tx != null) { tx.rollback(); } throw e; } finally { session.close(); } } /*根據(jù)OID找出指定customer_id的記錄*/ 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(); } } /* public void saveCustomerAndOrderWithCascade(){ Session session = sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); Customer customer=new Customer("Jack");//創(chuàng)建一個(gè)Customer持久化對象 //不保存customer對象,這樣執(zhí)行的話會出現(xiàn)異常 Order order1=new Order("Jack_Order001",customer); Order order2=new Order("Jack_Order002",customer);//創(chuàng)建兩個(gè)Order對象 session.save(order1); session.save(order2); tx.commit(); }catch (RuntimeException e) { if (tx != null) { tx.rollback(); } e.printStackTrace(); } finally { session.close(); } } */ public void saveCustomerAndOrder(){ Session session = sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); Customer customer=new Customer("Tom");//創(chuàng)建一個(gè)Customer持久化對象 session.save(customer); Order order1=new Order("Tom_Order001",customer); Order order2=new Order("Tom_Order002",customer);//創(chuàng)建兩個(gè)Order對象 session.save(order1); session.save(order2); // 對同一個(gè)customerHibernate執(zhí)行兩次插入ORDERS表 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(); Customer customer=findCustomer(1); List orders=findOrdersByCustomer(customer); printOrders(orders); } public static void main(String args[]){ new BusinessService().test(); sessionFactory.close(); } } <span style="font-size:16px;color:#cc33cc;"><strong> </strong></span>
上述代碼中方法 saveCustomerAndOrderWithCascade()如果沒有session.save(customer)這一句,
執(zhí)行時(shí)會拋出PropertyValueException異常,主要原因是:
在調(diào)用session.save(order1)方法之前,order1和customer對象都是臨時(shí)的,臨時(shí)對象是由new創(chuàng)建的,都是沒有持久化的對象。假設(shè) session.save(order1)被成功執(zhí)行,order1會被成功持久化,變成持久化對象,但是Hibernate不會自動持久化order1所關(guān)聯(lián)的customer對象。
在執(zhí)行session.save(order1)時(shí),插入ORDERS表記錄的CUSTOMER_ID字段為null,這違反了數(shù)據(jù)庫完整性約束,即ORDERS表中不允許CUSTOMER_ID為null。
疑問假設(shè)ORDERS表中CUSTOMER_ID字段允許為null:
<many-to-one name="customer" column="CUSTOMER_ID" class="mypack.Customer" not-null="false" lazy="false" />
這樣執(zhí)行的話,能夠成功的向ORDERS表中插入兩條數(shù)據(jù);但是當(dāng)Hibernate自動清理(flush)緩存中所有持久化對象時(shí),又會拋出新的異常
org.hibernate.TransientObjectException:object references an unsaved transient instance -save the transient instance before flushing :mypack.Customer
所謂清理是指Hibernate按照持久化對象的屬性變化來同步更新數(shù)據(jù)庫。在清理的時(shí)候Hibernate會發(fā)現(xiàn)order1和order2都引用臨時(shí)對象customer,而在ORDERS表中CUSTOMER_ID字段為null,這就意味著內(nèi)存中持久化對象的屬性和數(shù)據(jù)庫中記錄不一致。之所以會報(bào)錯是因?yàn)閛rder1中customer屬性引用了一個(gè)臨時(shí)對象Customer。
由此可見,Hibernate持久化一個(gè)對象時(shí),默認(rèn)情況下不會自動持久化所關(guān)聯(lián)的其他對象。但是,我們我們希望當(dāng)Hibernate持久化Order對象時(shí)自動持久化所關(guān)聯(lián)的Customer對象,我們可以修改映射文件如下:
<many-to-one name="customer" column="CUSTOMER_ID" class="mypack.Customer" cascade="save-update" not-null="false" lazy="false" />
當(dāng)cascade屬性為“save-update”,表明保存或更新對象時(shí),會級聯(lián)保存或更新與它所關(guān)聯(lián)的對象。如上例中,執(zhí)行saveCustomerAndOrderWithCascade()時(shí),Hibernate會把order1與customer對象一起持久化,此時(shí)Hibernate會執(zhí)行
insert into CUSTOMERS(ID,NAME) values(2,"Jack"); insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) value (3,"Jack_Order001",2);
二、映射一對多雙向關(guān)聯(lián)關(guān)系
類類之間建立了聯(lián)系,就可以很方便地從一個(gè)對象導(dǎo)航到另一個(gè)或者另一組與它相關(guān)聯(lián)的對象。正如上例中,對于給定的Order對象,如果想獲得與之關(guān)聯(lián)的Customer對象,可以直接如下調(diào)用:
Customer customer=order.getCustomer();
那么對于給定的Customer對象,如何一次獲得所有與之關(guān)聯(lián)的Order對象呢?由于上例中Customer對象沒有和Order對象關(guān)聯(lián),我們也可以通過Hibernate API去查詢數(shù)據(jù)庫:
List orders=session.createQuery("from Order as o where o.customer.id="+customer.getId()).list();
顯然這樣做的效率會很低,而且復(fù)雜的關(guān)聯(lián)關(guān)系也會給編程帶來影響。我們可以為Customer類和Order類簡歷一對多的雙向關(guān)聯(lián)。
第一部分我們已經(jīng)建立了Order類和Customer類的多對一關(guān)聯(lián),現(xiàn)在我們再增加Customer到Order類的一對多關(guān)聯(lián)。
Customer.java文件:
package mypack; import java.util.HashSet; import java.util.Set; //Hibernate要求在持久化類中定義集合類屬性時(shí),必須要把屬性聲明為接口類型。 public class Customer implements java.io.Serializable { private long id; private String name; private Set orders = new HashSet();//初始化為集合實(shí)現(xiàn)類,這樣做可以提高程序的健壯性,同時(shí)避免了應(yīng)用程序訪問取詞為null的orders集合的方法而拋出NullPointerException。 public Customer() { } public Customer(String name, Set orders) { this.name = name; this.orders = orders; } //省略了id,name的get和set訪問方法 public Set getOrders() { return this.orders; } public void setOrders(Set orders) { this.orders = orders; } }
接下來就是映射文件的配置Customer.hbm.xml:
<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" />//表示ORDERS表通過外鍵CUSTOMER_ID參照CUSTOMERS表 <one-to-many class="mypack.Order" /> </set> </class>
使用方法のBussiness.java演示分函數(shù)介紹:
(1)saveCustomerAndOrderWithCascade()方法:當(dāng)映射文件中<set>的屬性為“save-update”時(shí),Hibernate在持久化Customer對象時(shí)也會自動持久化其所關(guān)聯(lián)的Order對象
public void saveCustomerAndOrderWithCascade(){ Session session = sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); /*創(chuàng)建一個(gè)customer對象和order對象*/ Customer customer=new Customer("Tom",new HashSet()); Order order=new Order(); order.setOrderNumber("Tom_Order001"); /*建立Customer與Order的一對多雙向關(guān)聯(lián)關(guān)系*/ order.setCustomer(customer); customer.getOrders().add(order); /*保存Customer對象*/ session.save(customer); /* 當(dāng)映射文件中<set>的屬性為“save-update”時(shí),Hibernate在持久化Customer對象時(shí)也會自動持久化其所關(guān)聯(lián)的Order對象 insert into CUSTOMERS(ID,NAME) values(1,"Tom"); insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(1,"Tom_Order001",1)*/ tx.commit(); idOfTom=customer.getId(); idOfTomOrder=order.getId(); }catch (RuntimeException e) { if (tx != null) { tx.rollback(); } e.printStackTrace(); } finally { session.close(); } }
當(dāng)映射文件中<set>的屬性為“save-update”時(shí),Hibernate在持久化Customer對象時(shí)也會自動持久化其所關(guān)聯(lián)的Order對象
insert into CUSTOMERS(ID,NAME) values(1,"Tom"); insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(1,"Tom_Order001",1)
(2)printOrdersOfCustomer(Long customerId)方法:打印與指定customerId關(guān)聯(lián)的所有Order對象
public void printOrdersOfCustomer(Long customerId){ Session session = sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); Customer customer=(Customer)session.get(Customer.class,customerId); printOrders(customer.getOrders());//使用getOrders獲取一個(gè)order對象set tx.commit(); }catch (RuntimeException e) { if (tx != null) { tx.rollback(); } throw e; } finally { session.close(); } }
其調(diào)用的函數(shù)printOrders(Set orders)
public void printOrders(Set orders){ for (Iterator it = orders.iterator(); it.hasNext();) { Order order=(Order)it.next(); System.out.println("OrderNumber of "+order.getCustomer().getName()+ " :"+order.getOrderNumber()); } }
(3)saveCustomerAndOrderWithInverse()方法:演示映射文件<set>屬性為inverse
public void saveCustomerAndOrderWithInverse(){ saveCustomerAndOrderSeparately(); associateCustomerAndOrder(); }
調(diào)用的函數(shù)saveCustomerAndOrderSeparately():即是分別存儲,與saveCustomerAndOrderWithCascade()方法恰好相反。
Customer customer=new Customer(); customer.setName("Jack"); Order order=new Order(); order.setOrderNumber("Jack_Order001"); session.save(customer); session.save(order); tx.commit(); idOfJack=customer.getId(); idOfJackOrder=order.getId();
為了使上述代碼正常執(zhí)行,需要確保Order.hbm.xml文件的<many-to-one>元素的not null取默認(rèn)值false,否則會出現(xiàn)異常;Hibernate會執(zhí)行如下
insert into CUSTOMERS(ID,NAME) values(2,"Jack"); insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(2,"Jack_Order001",null);
調(diào)用的函數(shù)associateCustomerAndOrder():該方法加載由saveCustomerAndOrderSeparately()方法持久化Customer和Order對象,然后建立兩者之間的一對多的關(guān)系
public void associateCustomerAndOrder(){ Session session = sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); /*加載持久化對象Customer、Order*/ Customer customer=(Customer)session.load(Customer.class,idOfJack); Order order=(Order)session.load(Order.class,idOfJackOrder); /*建立Customer和Order的關(guān)聯(lián)關(guān)系*/ order.setCustomer(customer); customer.getOrders().add(order); tx.commit(); }catch (RuntimeException e) { if (tx != null) { tx.rollback(); } e.printStackTrace(); } finally { session.close(); } }
這樣重復(fù)執(zhí)行多余的SQL語句會影響java應(yīng)用的性能,解決的方法是將<set>的inverse屬性設(shè)為true。因此修改Customer.hbm.xml文件:
<set name="orders" inverse="true" cascade="save-update" > <key column="CUSTOMER_ID" />//表示ORDERS表通過外鍵CUSTOMER_ID參照CUSTOMERS表 <one-to-many class="mypack.Order" /> </set>
(4)級聯(lián)刪除:
tx = session.beginTransaction(); Customer customer=(Customer)session.load(Customer.class,customerId); session.delete(customer); tx.commit();
如果要刪除Customer所關(guān)聯(lián)的Order對象的話,需要將cascade屬性設(shè)置為delete,如下:
<set name="orders" inverse="true" cascade="delete" > <key column="CUSTOMER_ID" /> <one-to-many class="mypack.Order" /> </set>
執(zhí)行后,Hibernate會做以下動作:
delete from ORDERS where CUSTOMER_ID=2; delete from CUSTOMERS where ID=2;
如果關(guān)聯(lián)雙方是父子關(guān)系,就可以把復(fù)方的cascade設(shè)置為all-delete-orphan;這樣刪除父方對象時(shí)就會級聯(lián)刪除所有關(guān)聯(lián)的子方對象。
三、映射一對多雙向自身關(guān)聯(lián)關(guān)
Category.java:
package mypack; import java.util.HashSet; import java.util.Set; public class Category implements java.io.Serializable { private long id; private String name; private Set childCategories = new HashSet(0); private Category parentCategory; public Category() { } public Category(String name, Set childCategories, Category parentCategory) { this.name = name; this.childCategories = childCategories; this.parentCategory = parentCategory; } 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 getChildCategories() { return this.childCategories; } public void setChildCategories(Set childCategories) { this.childCategories = childCategories; } public Category getParentCategory() { return this.parentCategory; } public void setParentCategory(Category parentCategory) { this.parentCategory = parentCategory; } }
配置文件Category.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.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映射一對多關(guān)聯(lián)關(guān)系是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Hibernate映射一對多關(guān)聯(lián)關(guān)系是什么這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
文章名稱:Hibernate映射一對多關(guān)聯(lián)關(guān)系是什么
文章分享:http://muchs.cn/article2/gjghoc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營銷型網(wǎng)站建設(shè)、自適應(yīng)網(wǎng)站、品牌網(wǎng)站制作、網(wǎng)站維護(hù)、用戶體驗(yàn)、定制網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)