GONE WITH THE WIND

          --tomorrow is another day

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            30 隨筆 :: 19 文章 :: 0 評論 :: 0 Trackbacks

          構造這樣一個例子,在測試過程中來說明一些Hibernate的高級配置及其相關機制:
          有三個類:Category.java,Prodcuct.java,ConfigurationTest.java,其中第三個類是用來測試的。
          Category.java代碼:
                 

          package unsaved_value;    
          import ......    
          public class Category {    
              private Integer id;    
              private String name;    
              private String description;    
              private Set products;    
              public Category(){    
                   id=null;    
                   name=null;    
                  description=null;    
                  products=new HashSet ();    
               }    
               public void addProduct(Product p){    
                   products.add(p);    
              }    
               //**********setter and getter    
              ........    
          }   


          Product.java代碼:


          package unsaved_value;      
          public class Product {      
              private Integer id;      
              private String name;      
              private Category category;      
              private String description;      
              public Product(){     
                   
              }      
               //*******getter and setter      
               .........      
          }      

          ConfigurationTest.java

          public void testSave()throws Exception{    
                  Category category=new Category();    
                  category.setName("java編程書籍2");    
                  category.setDescription("編程經典書籍2");    
                  Product pro=new Product();    
                  pro.setName("java編程思想2");    
                  pro.setDescription("第四版中文版2");     
                  pro.setCategory(category);    
                  category.addProduct(pro);    
                  Transaction tx=session.beginTransaction();    
                  assert (session!=null):("session is null");    
                  session.save(category);    
                  tx.commit();    
              }    

               
          Category代表產品目錄,而Product代表產品,顯然Category與Product是一對多的關系。Hibernate在映射一對多關系時,有兩種方式,一種是單向一對多,一種是雙向關系。兩者相比,雙向一對多的好處體現在兩方面:首先,也是很明顯的一點,由于是雙向關聯,我們在實際業務邏輯時將更方便,例如我們可以檢索一個Category下的所有Product,同時還可以檢索出Product屬于哪個。其次,雙向關系相對單向關系而言,在數據庫的訪問方面更有優勢。這一點留在后面講inverse時講
          。雙向關聯比單向關聯唯一的”劣勢“,就在于雙向關聯需要比單向關聯多寫一個映射文件,這不問題。使用雙向關聯實現這兩個類同數據庫的映射:

          Category.hbm.xml:  
          version="1.0" encoding="UTF-8"?>  

          " <hibernate-mapping package="unsaved_value">  
             <class name="Category" table="category">  
               <id name="id" column="id">  
                 <generator class="native">generator>  
               id>  
               
               <property name="name" column="name"/>  
               <property name="description" column="description"/>


               <set name="products" table="product" lazy="true" inverse="true" cascade="all">  
                   <key column="category"/>  
                      <one-to-many class="Product"/>  
               set>  
               
             class>  
          hibernate-mapping>  
            
          Product.hbm.xml:


          version="1.0" encoding="UTF-8"?>    
          "
          <hibernate-mapping package="unsaved_value">    
          <class name="Product" table="product">    
               <id name="id" column="id" unsaved-value="null">    
                   <generator class="native">generator>    
               id>    
                   
               <property name="name" column="name"/>    
               <property name="description" column="description"/>    
                 
               <many-to-one name="category"    
                            column="category"     
                            class="Category"    
               />    
             class>    
          hibernate-mapping>    
                  
          現在把這個例子所牽涉到的知識一一展開:
          一.inverse
              該詞的譯意是“反轉”,反轉什么——反轉控制端,這項配置決定了由關聯雙方中的哪一方來維持關聯關系(在數據庫中表現為外鍵約束)。上述配置中,在Category.hbm.xml中將inverse設置為true,意思是說“我需要反轉(控制端)”,反轉的結果是由對方即Product來維持關聯關系。用單向關聯更容易說明”維持關聯關系“是什么意思:考慮用單向關系來實現這個映射關系的情況,即由Category關聯到Product,考慮下面的代碼:

          Product p=new Product();  
          ..setXXX  
          Category c=new Category();  
          ..設置Category的屬性  
          c.addProduct(p);//建立起了c和p的關聯關系  
          session.save(c);  

          會執行三條SQL語句:兩條插入語句,分別插入c和p,然后還有一條update語句建立起c和p的關聯(更新p的外鍵)。上面,我們說由Category端控制關聯,因此p.setCategory(c)這樣一句話是沒用的,它并不會導致在插入p的時候就設置p的外鍵以建立起兩者的關聯關系,從而節省一條update語句。同時我們還會看到,如果在數據庫模式中將p的外鍵設置成非空,這些代碼將不能執行,因為在插入p時,由于c和p的關聯關系還未建立起來,因此p的外鍵為空。回到雙向關聯上來,為了更清楚地明白inverse在雙向關聯中到底起什么作用,我們分別將其值設為true和false,看看打印出的的SQL有何區別:

          inverse=true時的打印結果:

          Hibernate: insert into category (name, description) values (?, ?)  
          Hibernate: insert into product (name, description, category) values (?, ?, ?)   
          inverse=false時的打印結果:

          Hibernate: insert into category (name, description) values (?, ?)    
          Hibernate: insert into product (name, description, category) values (?, ?, ?)    
          Hibernate: update product set category=? where id=?   
                 為什么inverse=true時會比inverse=false時少執行一條SQL語句?這是由控制端的不同造成的。前者說"我要反轉控制,由Product來控制關聯",因此在將p對象insert時,p已經設置了其category字段,從而建立了關聯關系,而后者說"我不反轉控制,由我自己來控制關聯",因此在將p對象insert后,c為了維持兩者的關聯,還要去執行一次update,以更新p的外鍵,從而建立起兩者的關聯關系。
          結論:對于一對多雙向關系,始終在“一”那一方將其inverse設置成true,這樣會提高性能。

          二.cascade
             級聯。當關聯的"一"方進行某種動作(更新,刪除)時,"多"方即使沒有顯式地進行編碼,它也會自動進行同樣的動作。cascade的可選值有:
          all : 所有情況下均進行關聯操作。即是save-update + delete
          none:所有情況下均不進行關聯操作。這是默認值。
          save-update:在執行save/update/saveOrUpdate時進行關聯操作。
          delete:在執行delete時進行關聯操作。
          all-delete-orphan:A:級聯save-update B級聯delete C:刪除所有孤兒項(orphan孤兒)。先看看父子關系,例如在Customer和Order的模型中,這兩者便是父子關系,當一個Customer的生命周期決定Order的生命周期,如果一個Customer不在了,其相關的Order繼續存在是毫無業務意義的。刪除所有孤兒項的意思即是,刪除所有與父對象失去關聯關系的子對象。

          三.lazy
              是否延遲加載。一般來說,應該延遲加載,即將lazy設為true。延遲加載的相關點很多,這在另外的學習筆記中總結。

          四.unsaved-value
              以上是"一"方的重要配置,再看看"多"方的一個重要配置:unsaved-value,就像上面Product.hbm.xml中的設置那樣,這一項在id的配置中設置。這一設置是與級聯一起工作的。關于這一點,robbin講的很清楚:
          當你顯式的使用session.save()或者session.update()操作一個對象的時候,實際上是用不到unsaved-value 的。某些情況下(父子表關聯保存),當你在程序中并沒有顯式的使用save或者update一個持久對象,那么Hibernate需要判斷被操作的對象究竟是一個已經持久化過的持久對象,是一個尚未被持久化過的內存臨時對象。例如:
                 Session session = ...;
          Transaction tx = ...;  
          Parent parent = (Parent) session.load(Parent.class, id);  
          Child child = new Child();  
          child.setParent(parent);  
          child.setName("sun");  
          parent.addChild(child);  
          s.update(parent);  
          s.flush();  
          tx.commit();  
          s.close();  
               在上例中,程序并沒有顯式的session.save(child); 那么Hibernate需要知道child究竟是一個臨時對象,還是已經在數據庫中有的持久對象。如果child是一個新創建的臨時對象(本例中就是這種情況),那么Hibernate應該自動產生session.save(child)這樣的操作,如果child是已經在數據庫中有的持久對象,那么 Hibernate應該自動產生session.update(child)這樣的操作。因此我們需要暗示一下Hibernate,究竟 child對象應該對它自動save還是update。在上例中,顯然我們應該暗示Hibernate對child自動save,而不是自動 update。那么Hibernate如何判斷究竟對child是save還是update呢?它會取一下child的主鍵屬性 child.getId() ,這里假設id是 java.lang.Integer類型的。如果取到的Id值和hbm映射文件中指定的unsave-value相等,那么Hibernate認為 child是新的內存臨時對象,發送save,如果不相等,那么Hibernate認為child是已經持久過的對象,發送update。unsaved-value="null" (默認情況,適用于大多數對象類型主鍵 Integer/Long/String/...)
          當Hibernate取一下child的Id,取出來的是null(在上例中肯定取出來的是null),和unsaved-value設定值相等,發送save(child)
          當Hibernate取一下child的id,取出來的不是null,那么和unsaved-value設定值不相等,發送update(child)
             unsaved-value的可選配置有:
          none,any,null
          unsaved-value="none"和unsaved-value="any"主要用在主鍵屬性不是通過Hibernate生成,而是程序自己setId()的時候。unsaved-value="none"和unsaved-value="any"究竟有什么含義了。如果你非要用assigned不可,那么繼續解釋一下:
          unsaved-value="none" 的時候,由于不論主鍵屬性為任何值,都不可能為none,因此Hibernate總是對child對象發送update(child)
          unsaved-value="any" 的時候,由于不論主鍵屬性為任何值,都肯定為any,因此Hibernate總是對child對象發送save(child)
                大多數情況下,可以避免使用assigned,只有當你使用復合主鍵的時候不得不手工setId(),這時候需要你自己考慮究竟怎么設置unsaved-value了,根據你自己的需要來定。
                關于為什么不要使主鍵帶有義務意義,robbin的解釋很清楚:還是以上面的例子打比方,如果我們將Category的某一個性質(比如產品序號或者名稱)作為主鍵,如果后來由于業務需要,我們把這個性質改了,那將不可僻免地要去修改與這個對象相關聯的所有數據的外鍵,而如果我們只要代理主鍵,這個問題就可完全僻免。

          主站蜘蛛池模板: 克拉玛依市| 海丰县| 赤峰市| 屏山县| 封丘县| 新田县| 博爱县| 青神县| 封开县| 平阴县| 建昌县| 吉隆县| 泸溪县| 万载县| 临西县| 尼玛县| 敦化市| 西贡区| 安吉县| 南充市| 长治市| 淮滨县| 祥云县| 伊金霍洛旗| 奇台县| 淳安县| 浏阳市| 马龙县| 白河县| 子洲县| 加查县| 凤山市| 石林| 石嘴山市| 绥棱县| 三江| 麻江县| 靖西县| 洞头县| 呼伦贝尔市| 龙胜|