今天在javaeye上看到了這個文章,感覺超好.做下來看看....
前不久在搭建系統框架的時候遇到one-many與many-one的關係中一個比較麻煩的問題,就是inverse的設置,看到在這裏也有許多人提出這個問題,也有許多解決方法,自己總結總結以
後作爲資料庫吧。
1.現在假設有兩個類Customer與Order,一個Customer可以有多個Order
2.
如果在Customer.hbm.xml中設置inverse=投入額,那麼代表customer與order的關係由order來維護,就是說如果採用
Customer c=customerDao.findById("1"),c.getOrders.add(new
Order()),那麼在數據庫層面表現爲,首先假設在order表格中customer_id可以爲空,那麼上面的代碼會在order表中重保存一條
order記錄,但是這條紀錄中的customer_id=null,這正體現出inverse的設置作用,customer的保存不會維護
ustomer與order之間的關係。
?
3.如果在Customer.hbm.xml中設置iverser=false,那麼代表customer與order的關係由customer來維護,
還是採用2種說明的代碼,那麼在數據庫中表現爲會增加一條order記錄,並且這條記錄的customer_d不爲空.
?
具體代碼說明:
1.模型類
- public?class?Customer?{??
- ??
- ????private?String?id;??
- ??
- ????private?String?name;??
- ??
- ????private?Set?orders?=?new?HashSet();??
- ??
- ????public?String?getId()?{??
- ????????return?id;??
- ????}??
- ??
- ????public?void?setId(String?id)?{??
- ????????this.id?=?id;??
- ????}??
- ??
- ????public?String?getName()?{??
- ????????return?name;??
- ????}??
- ??
- ????public?void?setName(String?name)?{??
- ????????this.name?=?name;??
- ????}??
- ??
- ????public?Set?getOrders()?{??
- ????????return?orders;??
- ????}??
- ??
- ????public?void?setOrders(Set?orders)?{??
- ????????this.orders?=?orders;??
- ????}??
- }??
public class Customer { private String id; private String name; private Set orders = new HashSet(); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set getOrders() { return orders; } public void setOrders(Set orders) { this.orders = orders; } }
?
?
- public?class?Order?{??
- ??
- ????private?String?id;??
- ??
- ????private?Customer?customer;??
- ??
- ????private?String?address;??
- ??
- ????public?String?getAddress()?{??
- ????????return?address;??
- ????}??
- ??
- ????public?void?setAddress(String?address)?{??
- ????????this.address?=?address;??
- ????}??
- ??
- ????public?Customer?getCustomer()?{??
- ????????return?customer;??
- ????}??
- ??
- ????public?void?setCustomer(Customer?customer)?{??
- ????????this.customer?=?customer;??
- ????}??
- ??
- ????public?String?getId()?{??
- ????????return?id;??
- ????}??
- ??
- ????public?void?setId(String?id)?{??
- ????????this.id?=?id;??
- ????}??
- }??
public class Order { private String id; private Customer customer; private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public String getId() { return id; } public void setId(String id) { this.id = id; } }?
?
1.配置文件
- "1.0"?encoding="utf-8"?>??
- "-//Hibernate/Hibernate?Mapping?DTD?3.0//EN"??
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">??
- ??
- <hibernate-mapping>??
- ????<class?name="domain.Customer"?table="customers"?catalog="blogday">??
- ????????<id?name=<span>"id">??
- ????????????<column?name=<span>"id"?/>??
- ????????????<generator?<span>class="native"></generator>??
- ????????</id>??
- ????????<property?name=<span>"name">??
- ????????????<column?name=<span>"name"?length="20"?/>??
- ????????</property>??
- ????????<set?name=<span>"orders"?lazy="true"?inverse="true"?cascade="all">??
- ????????????<key?column=<span>"customer_id"?/>??
- ????????????<one-to-many?<span>class="domain.Order"?/>??
- ????????</set>??
- ????class>??
- </hibernate-mapping>??
- ???
<hibernate-mapping> <class name="domain.Customer" table="customers" catalog="blogday"> <id name="id"> <column name="id"> <generator class="native"></generator> </id> <property name="name"> <column name="name" length="20"> </property> <set name="orders" lazy="true" inverse="true" cascade="all"> <key column="customer_id"> <one-to-many class="domain.Order"> </set> </class> </hibernate-mapping> ??
- "1.0"?encoding="utf-8"?>??
- "-//Hibernate/Hibernate?Mapping?DTD?3.0//EN"??
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">??
- ??
- <hibernate-mapping>??
- ????<class?name="domain.Order"?table="orders"?catalog="blogday">??
- ????????<id?name=<span>"id">??
- ????????????<column?name=<span>"id"?/>??
- ????????????<generator?<span>class="native"></generator>??
- ????????</id>??
- ????????<property?name=<span>"address">??
- ????????????<column?name=<span>"address"?length="20"?/>??
- ????????</property>??
- ????????<many-to-one?name=<span>"customer"?column="customer_id"??
- ????????????class="domain.Customer"?cascade="none"?/>??
- ????class>??
- </hibernate-mapping>??
<hibernate-mapping> <class name="domain.Order" table="orders" catalog="blogday"> <id name="id"> <column name="id"> <generator class="native"></generator> </id> <property name="address"> <column name="address" length="20"> </property> <many-to-one name="customer" column="customer_id" class="domain.Customer" cascade="none"> </class> </hibernate-mapping>?
3.測試代碼
- public?void?testSaveOrdersByInverse_Error_Method()?{??
- ????????Exception?e?=?null;??
- ????????Customer?c?=?customerDao.findById("1");??
- ????????Order?order?=?new?Order();??
- ????????Order?order2?=?new?Order();??
- ????????c.getOrders().add(order2);??
- ????????c.getOrders().add(order);??
- ????????try?{??
- ????????????customerDao.save(c);??
- ????????????customerDao.getHibernateTemplate().flush();??
- ????????}?catch?(Exception?e1)?{??
- ????????????e?=?e1;??
- ????????}??
- ????????assertNotNull(e);??
- ????}??
- ???????
public void testSaveOrdersByInverse_Error_Method() { Exception e = null; Customer c = customerDao.findById("1"); Order order = new Order(); Order order2 = new Order(); c.getOrders().add(order2); c.getOrders().add(order); try { customerDao.save(c); customerDao.getHibernateTemplate().flush(); } catch (Exception e1) { e = e1; } assertNotNull(e); } ?
4. 結果說明
?
4.1? 如果設置invserse=true,那麼方法testSaveOrdersByInverse_Error_Method()在測試的時候
customerDao.getHibernateTemplate().flush()進行flush的時候會發生錯誤,錯誤原因是在order表格中
不 允許customer_id=null,而在此時方法testSaveOrdersByInverse_Error_Method會級聯保存order記 錄,但是保存的order記錄中hibernate卻設置customer_id=null,這也說明了inverse=true說明customer不 負責與order之間的引用關係,只是簡單的保存記錄,並沒有建立起二者之間的關係.
?
4.2如果設置 inverse=false,說明customer與order之間的關係由customer來維護,那麼上面的測試方法 testSaveOrdersByInverse_Error_Method()不但會保存相應的order記錄,並且order記錄中的 customer_id也不爲空.
?
5.性能考慮
?
5.1 如果設置inverse=true,用如下代碼測試
- public?void?testSaveOrdersByInverse_Right_method()?{??
- ????????Exception?e?=?null;??
- ????????Customer?c?=?customerDao.findById("1");??
- ????????Order?order1?=?new?Order();??
- ????????Order?order2?=?new?Order();??
- ????????c.getOrders().add(order1);??
- ????????c.getOrders().add(order2);??
- ????????order1.setCustomer(c);??
- ????????order2.setCustomer(c);??
- ????????try?{??
- ????????????customerDao.save(c);??
- ????????????customerDao.getHibernateTemplate().flush();??
- ????????}?catch?(Exception?e1)?{??
- ????????????e?=?e1;??
- ????????}??
- ????????assertNull(e);??
- ????}??
public void testSaveOrdersByInverse_Right_method() { Exception e = null; Customer c = customerDao.findById("1"); Order order1 = new Order(); Order order2 = new Order(); c.getOrders().add(order1); c.getOrders().add(order2); order1.setCustomer(c); order2.setCustomer(c); try { customerDao.save(c); customerDao.getHibernateTemplate().flush(); } catch (Exception e1) { e = e1; } assertNull(e); }
?? 測試結果爲:
?
- Hibernate:?select?orders0_.customer_id?as?customer3_1_,?orders0_.id?as?id1_,?orders0_.id?as?id1_0_,?orders0_.address?as?address1_0_,?orders0_.customer_id?as?customer3_1_0_?from?blogday.orders?orders0_?where?orders0_.customer_id=???
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
Hibernate: select orders0_.customer_id as customer3_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.address as address1_0_, orders0_.customer_id as customer3_1_0_ from blogday.orders orders0_ where orders0_.customer_id=? Hibernate: insert into blogday.orders (address, customer_id) values (?, ?) Hibernate: insert into blogday.orders (address, customer_id) values (?, ?)??? 說明沒插入一條order只需要執行一次插入操作,沒有額外的代碼執行.
?
5.2 如果設置inverse=false,也順便設置數據庫中order表格中customer_id可以爲空,這樣下面的代碼纔不會 拋出錯誤
- public?void?testSaveOrdersByInverse_Error_Method()?{??
- ????????Exception?e?=?null;??
- ????????Customer?c?=?customerDao.findById("1");??
- ????????Order?order1?=?new?Order();??
- ????????Order?order2?=?new?Order();??
- ????????c.getOrders().add(order1);??
- ????????c.getOrders().add(order2);??
- ????????try?{??
- ????????????customerDao.save(c);??
- ????????????customerDao.getHibernateTemplate().flush();??
- ????????}?catch?(Exception?e1)?{??
- ????????????e?=?e1;??
- ????????}??
- ????????assertNull(e);??
- ????}??
public void testSaveOrdersByInverse_Error_Method() { Exception e = null; Customer c = customerDao.findById("1"); Order order1 = new Order(); Order order2 = new Order(); c.getOrders().add(order1); c.getOrders().add(order2); try { customerDao.save(c); customerDao.getHibernateTemplate().flush(); } catch (Exception e1) { e = e1; } assertNull(e); }
?測試結果:
- Hibernate:?select?customer0_.id?as?id0_0_,?customer0_.name?as?name0_0_?from?blogday.customers?customer0_?where?customer0_.id=???
- Hibernate:?select?orders0_.customer_id?as?customer3_1_,?orders0_.id?as?id1_,?orders0_.id?as?id1_0_,?orders0_.address?as?address1_0_,?orders0_.customer_id?as?customer3_1_0_?from?blogday.orders?orders0_?where?orders0_.customer_id=???
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
- Hibernate:?update?blogday.orders?set?customer_id=??where?id=???
- Hibernate:?update?blogday.orders?set?customer_id=??where?id=???
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_ from blogday.customers customer0_ where customer0_.id=? Hibernate: select orders0_.customer_id as customer3_1_, orders0_.id as id1_, orders0_.id as id1_0_, orders0_.address as address1_0_, orders0_.customer_id as customer3_1_0_ from blogday.orders orders0_ where orders0_.customer_id=? Hibernate: insert into blogday.orders (address, customer_id) values (?, ?) Hibernate: insert into blogday.orders (address, customer_id) values (?, ?) Hibernate: update blogday.orders set customer_id=? where id=? Hibernate: update blogday.orders set customer_id=? where id=?
?
?
插入customer也會相應的插入order記錄,這個在上面已經說明.但是看這裏多出了兩條update語句,針對每一個孩子都去更新父親的id明顯速度很慢,因爲父親有個孩子的集合,他無法知道哪個孩子的父親id已經指向自己了,所以對於每一個孩子,都要更新父 親使他只想自己,而這個關係由孩子維護就好多了,每個孩子只有一個父親,只有設置過的才需要更新,所以顯然,這個父子關係由孩子來維護比較省力.減輕了數 據庫的負擔.
?
?
正對以上情況在設置one-many或者many-one或者many-many的時候需要謹慎用inverse,而且從性能上來說一般還是有字節點來維護與父節點的關係比較好如下代碼就可以簡單而且高效來建立二者之間的關係.
?
- public?void?testSave()?{??
- ????Customer?customer?=?customerDao.findById("1");??
- ????Order?order?=?new?Order();??
- ????order.setCustomer(customer);??
- ????orderDao.save(order);??
- ????assertNotNull(order.getId());??
- }??
public void testSave() { Customer customer = customerDao.findById("1"); Order order = new Order(); order.setCustomer(customer); orderDao.save(order); assertNotNull(order.getId()); }?
這裏的操作很簡單而且數據庫操作也僅有一條查詢語句和一條插入語句.
?
- Hibernate:?select?customer0_.id?as?id0_0_,?customer0_.name?as?name0_0_?from?blogday.customers?customer0_?where?customer0_.id=???
- Hibernate:?insert?into?blogday.orders?(address,?customer_id)?values?(?,??)??
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_ from blogday.customers customer0_ where customer0_.id=? Hibernate: insert into blogday.orders (address, customer_id) values (?, ?)?
?
?
?
?