您好,登錄后才能下訂單哦!
這篇文章主要講解了“Hibernate映射一對多關聯關系是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Hibernate映射一對多關聯關系是什么”吧!
在域模型中,類和類之間最普通的關系就是關聯關系。在UML語言中,關聯是有方向的。以客戶(Customer)和訂單(Order)的關系為例,一個客戶可以發出多個訂單,而一個訂單只能屬于一個客戶。
從Order到Customer的關聯是多對一關聯,這意味著每個Order對象都會引用一個Customer對象,因此在Order類中應該定義一個Customer類型的屬性,來引用所關聯的Customer對象。
從Customer到Order的關聯是一對多的關聯,這意味著每個Customer對象都會引用一組Order對象,因此在Customer類中應該定義一個集合類型的屬性,來引用所有關聯的Order對象。
一、建立多對一的單向關聯關系
如上例中,我們只需在Order類中定義一個customer屬性,而在Customer類中無需定義存放Order對象的集合屬性。
Order.java
package mypack; public class Order implements java.io.Serializable { private long id; private String orderNumber; private Customer customer;//定義一個Customer屬性 public Order() { } public Order(Customer customer) { this.customer = customer; } public Order(String orderNumber, Customer customer) { this.orderNumber = orderNumber; this.customer = customer; } //省略了id,orderNumber的構造方法 public Customer getCustomer() { return this.customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
Customer類的所有屬性都是和CUSTOMERS表中的字段一一對應,因此可以直接使用如下的映射代碼:
<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字段對應,映射代碼和上面類似,此處省去。我們關注的主要地方是,Order類中的customer屬性,因為他是Customer類型的,是與ORDERS表的外鍵CUSTOMER_ID對應的,它的真實值是存在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;} } /*根據參數指定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執行: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(); } } /*根據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");//創建一個Customer持久化對象 //不保存customer對象,這樣執行的話會出現異常 Order order1=new Order("Jack_Order001",customer); Order order2=new Order("Jack_Order002",customer);//創建兩個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");//創建一個Customer持久化對象 session.save(customer); Order order1=new Order("Tom_Order001",customer); Order order2=new Order("Tom_Order002",customer);//創建兩個Order對象 session.save(order1); session.save(order2); // 對同一個customerHibernate執行兩次插入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)這一句,
執行時會拋出PropertyValueException異常,主要原因是:
在調用session.save(order1)方法之前,order1和customer對象都是臨時的,臨時對象是由new創建的,都是沒有持久化的對象。假設 session.save(order1)被成功執行,order1會被成功持久化,變成持久化對象,但是Hibernate不會自動持久化order1所關聯的customer對象。
在執行session.save(order1)時,插入ORDERS表記錄的CUSTOMER_ID字段為null,這違反了數據庫完整性約束,即ORDERS表中不允許CUSTOMER_ID為null。
疑問假設ORDERS表中CUSTOMER_ID字段允許為null:
<many-to-one name="customer" column="CUSTOMER_ID" class="mypack.Customer" not-null="false" lazy="false" />
這樣執行的話,能夠成功的向ORDERS表中插入兩條數據;但是當Hibernate自動清理(flush)緩存中所有持久化對象時,又會拋出新的異常
org.hibernate.TransientObjectException:object references an unsaved transient instance -save the transient instance before flushing :mypack.Customer
所謂清理是指Hibernate按照持久化對象的屬性變化來同步更新數據庫。在清理的時候Hibernate會發現order1和order2都引用臨時對象customer,而在ORDERS表中CUSTOMER_ID字段為null,這就意味著內存中持久化對象的屬性和數據庫中記錄不一致。之所以會報錯是因為order1中customer屬性引用了一個臨時對象Customer。
由此可見,Hibernate持久化一個對象時,默認情況下不會自動持久化所關聯的其他對象。但是,我們我們希望當Hibernate持久化Order對象時自動持久化所關聯的Customer對象,我們可以修改映射文件如下:
<many-to-one name="customer" column="CUSTOMER_ID" class="mypack.Customer" cascade="save-update" not-null="false" lazy="false" />
當cascade屬性為“save-update”,表明保存或更新對象時,會級聯保存或更新與它所關聯的對象。如上例中,執行saveCustomerAndOrderWithCascade()時,Hibernate會把order1與customer對象一起持久化,此時Hibernate會執行
insert into CUSTOMERS(ID,NAME) values(2,"Jack"); insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) value (3,"Jack_Order001",2);
二、映射一對多雙向關聯關系
類類之間建立了聯系,就可以很方便地從一個對象導航到另一個或者另一組與它相關聯的對象。正如上例中,對于給定的Order對象,如果想獲得與之關聯的Customer對象,可以直接如下調用:
Customer customer=order.getCustomer();
那么對于給定的Customer對象,如何一次獲得所有與之關聯的Order對象呢?由于上例中Customer對象沒有和Order對象關聯,我們也可以通過Hibernate API去查詢數據庫:
List orders=session.createQuery("from Order as o where o.customer.id="+customer.getId()).list();
顯然這樣做的效率會很低,而且復雜的關聯關系也會給編程帶來影響。我們可以為Customer類和Order類簡歷一對多的雙向關聯。
第一部分我們已經建立了Order類和Customer類的多對一關聯,現在我們再增加Customer到Order類的一對多關聯。
Customer.java文件:
package mypack; import java.util.HashSet; import java.util.Set; //Hibernate要求在持久化類中定義集合類屬性時,必須要把屬性聲明為接口類型。 public class Customer implements java.io.Serializable { private long id; private String name; private Set orders = new HashSet();//初始化為集合實現類,這樣做可以提高程序的健壯性,同時避免了應用程序訪問取詞為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演示分函數介紹:
(1)saveCustomerAndOrderWithCascade()方法:當映射文件中<set>的屬性為“save-update”時,Hibernate在持久化Customer對象時也會自動持久化其所關聯的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); /*保存Customer對象*/ session.save(customer); /* 當映射文件中<set>的屬性為“save-update”時,Hibernate在持久化Customer對象時也會自動持久化其所關聯的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(); } }
當映射文件中<set>的屬性為“save-update”時,Hibernate在持久化Customer對象時也會自動持久化其所關聯的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關聯的所有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獲取一個order對象set tx.commit(); }catch (RuntimeException e) { if (tx != null) { tx.rollback(); } throw e; } finally { session.close(); } }
其調用的函數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(); }
調用的函數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();
為了使上述代碼正常執行,需要確保Order.hbm.xml文件的<many-to-one>元素的not null取默認值false,否則會出現異常;Hibernate會執行如下
insert into CUSTOMERS(ID,NAME) values(2,"Jack"); insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(2,"Jack_Order001",null);
調用的函數associateCustomerAndOrder():該方法加載由saveCustomerAndOrderSeparately()方法持久化Customer和Order對象,然后建立兩者之間的一對多的關系
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的關聯關系*/ order.setCustomer(customer); customer.getOrders().add(order); tx.commit(); }catch (RuntimeException e) { if (tx != null) { tx.rollback(); } e.printStackTrace(); } finally { session.close(); } }
這樣重復執行多余的SQL語句會影響java應用的性能,解決的方法是將<set>的inverse屬性設為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)級聯刪除:
tx = session.beginTransaction(); Customer customer=(Customer)session.load(Customer.class,customerId); session.delete(customer); tx.commit();
如果要刪除Customer所關聯的Order對象的話,需要將cascade屬性設置為delete,如下:
<set name="orders" inverse="true" cascade="delete" > <key column="CUSTOMER_ID" /> <one-to-many class="mypack.Order" /> </set>
執行后,Hibernate會做以下動作:
delete from ORDERS where CUSTOMER_ID=2; delete from CUSTOMERS where ID=2;
如果關聯雙方是父子關系,就可以把復方的cascade設置為all-delete-orphan;這樣刪除父方對象時就會級聯刪除所有關聯的子方對象。
三、映射一對多雙向自身關聯關
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映射一對多關聯關系是什么”的內容了,經過本文的學習后,相信大家對Hibernate映射一對多關聯關系是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。