正確理解Hibernate Inverse (轉)

langgufu314發表於2012-12-05

通過Hibernate Inverse的設定來決定是由誰來維護表和表之間的關係。最近有朋友問我Hibernate關於多對多關於刪除中間表資料的問題,關鍵是Inverse的設定,下面引用網友的一篇文章。

Inverse是Hibernate雙向關係中的基本概念,當然對於多數實體,我們並不需要雙向關聯,更多的可能會選擇單向關聯,況且我們大多數人 一般採用一對多關係,而一對多雙向關聯的另一端:多對一的Inverse屬性是不存在,其實它預設就是Inverse=false.從而防止了在一對多端 胡亂設定Inverse也不至於出錯。但是Inverse設定不當確實會帶來很大的效能影響,這點是我們必須關注的。

這篇文章已經詳細分析了Hibernate Inverse設定不當帶來的影響:http://www.Hibernate.org/155.html,看了這篇文章,還是很有必要再寫下一些總結的:

1)Hibernate Inverse中提及的side其實是指一個類或者表的概念,雙向關聯其實是指雙方都可以取得對方的應用。

2)維護關係這個名詞還是稍顯模糊或者晦澀。我們一般說A類或者A表(這裡的表的是指多對多的連線表)有責任維護關係,其實這裡的意思是說,我在應 用在更新,建立,刪除(讀就不用說了,雙向引用正是為了方便讀而出現)A類或者A表時,此時建立的SQL語句必須有責任保證關係的正確修改。

3)Inverse=false的side(side其實是指Inverse=false所位於的class元素)端有責任維護關係,而Inverse=true端無須維護這些關係。

4)我們說Hibernate Inverse設立不當會導致效能低下,其實是說Inverse設立不當,會產生多餘重複的SQL語句甚至致使JDBC exception的throw。這是我們在建立實體類關係時必須需要關注的地方。一般來說,Inverse=true是推薦使用,雙向關聯中雙方都設定 Inverse=false的話,必會導致雙方都重複更新同一個關係。但是如果雙方都設立Inverse=true的話,雙方都不維護關係的更新,這也是 不行的,好在一對多中的一端:many-to-one預設是Inverse=false,避免了這種錯誤的產生。但是多對多就沒有這個預設設定了,所以很 多人經常在多對多的兩端都使用Inverse=true,結果導致連線表的資料根本沒有記錄,就是因為他們雙分都沒有責任維護關係。所以說,雙向關聯中最 好的設定是一端為Inverse=true,一端為Inverse=false。一般Inverse=false會放在多的一端,那麼有人提問了, many-to-many兩邊都是多的,Inverse到底放在哪兒?其實Hibernate建立多對多關係也是將他們分離成兩個一對多關係,中間連線一 個連線表。所以通用存在一對多的關係,也可以這樣說:一對多是多對多的基本組成部分。

看下面的多對多的定義大家更會清楚”多對多“與“一對多”的關係:其中我們注意<many-to-many />標籤的特點就知道,它是定義了一個多對多關係,而不是<one-to-many/>。

  1. <?xml version="1.0"?>
  2. <!DOCTYPE Hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
  4. "http://Hibernate.sourceforge.net/Hibernate-mapping-2.0.dtd">
  5. <Hibernate-mapping package="org.Hibernate.auction">
  6. <class name="TestA" table="TestA"
  7. dynamic-update="true" dynamic-insert="true" >
  8. <id name="id" column="id" type="int" unsaved-value="any" >
  9. <generator class="assigned">
  10. </generator>
  11. </id>
  12. <property name="name" type="java.lang.String"
  13. update="true" insert="true" column="name" />
  14. <set name="testBs" table="TestA_TestB" Inverse="false" cascade="all">
  15. <key column="testA"/>
  16. <many-to-many column="testB" class="TestB" />
  17. </set>
  18. </class>
  19. <class name="TestB" table="TestB"
  20. dynamic-update="true" dynamic-insert="true" >
  21. <id name="id" column="id" type="int" unsaved-value="any" >
  22. <generator class="assigned">
  23. </generator>
  24. </id>
  25. <property name="name" type="java.lang.String" update="true"
  26. insert="true" column="name" />
  27. <set name="testAs" table="TestA_TestB" Inverse="true" cascade="all">
  28. <key column="testB"/>
  29. <many-to-many column="testA" class="TestA" />
  30. </set>
  31. </class>
  32. </Hibernate-mapping>

在對多對中,因為一端維護關係另一端不維護關係的原因,我們必須注意避免在應用中用不維護關係的類建立關係,因為這樣建立的關係是不會在資料庫中儲存的。基於上面的對映檔案程式碼給出一個例子:

  1. package org.Hibernate.auction;
  2. import java.util.*;
  3. /**
  4. * @author Administrator
  5. *
  6. * To change the template for this generated type comment go to
  7. * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
  8. */
  9. public class TestA {
  10. int id;
  11. String name;
  12. Set testBs=new HashSet();
  13. public TestA(){
  14. }
  15. public TestA(int id){
  16. setId(id);
  17. }
  18. public int getId(){
  19. return id;
  20. }
  21. public void setId(int id){
  22. this.id=id;
  23. }
  24. public String getName(){
  25. return name;
  26. }
  27. public void setName(String name){
  28. this.name=name;
  29. }
  30. public Set getTestBs(){
  31. return testBs;
  32. }
  33. public void setTestBs(Set s){
  34. testBs=s;
  35. }
  36. public void addTestB(TestB tb){
  37. testBs.add(tb);
  38. }public static void main(String[] args) {
  39. }
  40. }
  41. public class TestB {
  42. int id;
  43. String name;
  44. Set testAs=new HashSet();
  45. public TestB(){
  46. }
  47. public TestB(int id){
  48. setId(id);
  49. }
  50. public int getId(){
  51. return id;
  52. }
  53. public void setId(int id){
  54. this.id=id;
  55. }
  56. public String getName(){
  57. return name;
  58. }
  59. public void setName(String name){
  60. this.name=name;
  61. }
  62. public Set getTestAs(){
  63. return testAs;
  64. }
  65. public void setTestAs(Set s){
  66. testAs=s;
  67. }
  68. public void addTestA(TestA ta){
  69. testAs.add(ta);
  70. }
  71. public static void main(String[] args) {
  72. }
  73. }

測試程式碼:

  1. public void doTest() throws Exception{
  2. TestA a1=new TestA(1);
  3. TestA a2=new TestA(2);
  4. TestA a3=new TestA(3);
  5. TestB b1=new TestB(1);
  6. TestB b2=new TestB(2);
  7. TestB b3=new TestB(3);
  8. a1.addTestB(b1);
  9. a1.addTestB(b2);
  10. a1.addTestB(b3);
  11. b2.addTestA(a1);
  12. b2.addTestA(a2);
  13. Session s = factory.openSession();
  14. s = factory.openSession();
  15. Session session = factory.openSession();
  16. session.save(a1);
  17. session.flush();
  18. session.close();
  19. }

測試後連線表的資料為:

testa testb

1 1

1 2

1 3

根據Inverse規則,對這些程式碼:b2.addTestA(a1); b2.addTestA(a2); 建立的關係,資料庫並沒有儲存下來,因為TestB沒有責任維護這些關係,所以產生的sql語句自然不會有針對Testa_testB表的操作了。假設應 用中真的需要這些方法,那麼我們可以修改TestB的方法,讓他們注意在維護端類中執行相應的操作以使得關係能夠在資料庫中儲存下來,更改TestB如 下:

  1. /*
  2. * Created on 2004-7-25
  3. *
  4. * To change the template for this generated file go to
  5. * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
  6. */
  7. package org.Hibernate.auction;
  8. import java.util.*;
  9. /**
  10. * @author Administrator
  11. *
  12. * To change the template for this generated type comment go to
  13. * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
  14. */
  15. public class TestB {
  16. int id;
  17. String name;
  18. Set testAs=new HashSet();
  19. public TestB(){
  20. }
  21. public TestB(int id){
  22. setId(id);
  23. }
  24. public int getId(){
  25. return id;
  26. }
  27. public void setId(int id){
  28. this.id=id;
  29. }
  30. public String getName(){
  31. return name;
  32. }
  33. public void setName(String name){
  34. this.name=name;
  35. }
  36. public Set getTestAs(){
  37. return testAs;
  38. }
  39. public void setTestAs(Set s){
  40. testAs=s;
  41. }
  42. public void addTestA(TestA ta){
  43. testAs.add(ta);
  44. ta.addTestB(this);
  45. }
  46. public static void main(String[] args) {
  47. }
  48. }

那麼測試執行後連線表的資料為:

testa testb

1 2

1 3

1 1

2 2

測試通過。

相關文章