天天看點

Hibernate - 對象關系映射檔案(*.hbm.xml)詳解

對象關系映射檔案,即POJO 類和資料庫的映射檔案​

​*.hbm.xml​

​(映射檔案的擴充名為 .hbm.xml)。

POJO 類和關系資料庫之間的映射可以用一個XML文檔來定義。

通過 POJO 類的資料庫映射檔案,Hibernate可以了解持久化類和資料表之間的對應關系,也可以了解持久化類屬性與資料庫表列之間的對應關系。

在運作時 Hibernate 将根據這個映射檔案來生成各種 SQL 語句。

常見結構如下:

hibernate-mapping
  -類層次:class
    --主鍵:id
    --基本類型:property
    --實體引用類: many-to-one  |  one-to-one
    --集合:set | list | map | array
      ---one-to-many
      ---many-to-many
    --子類:subclass | joined-subclass
    --其它:component | any 等  
  -查詢語句:query(用來放置查詢語句,便于對資料庫查詢的統一管理和優化)      

每個Hibernate-mapping中可以同時定義多個類. 但更推薦為每個類都建立一個單獨的映射檔案。

【1】 hibernate-mapping節點

​hibernate-mapping節點​

​屬性如下:

Hibernate - 對象關系映射檔案(*.hbm.xml)詳解

hibernate-mapping 是 hibernate 映射檔案的根元素。

  • schema: 指定所映射的資料庫schema的名稱。若指定該屬性, 則表明會自動添加該 schema 字首。
  • catalog:指定所映射的資料庫catalog的名稱。
  • default-cascade(預設為 none): 設定hibernate預設的級聯風格. 若配置 Java 屬性, 集合映射時沒有指定 cascade 屬性, 則 Hibernate 将采用此處指定的級聯風格。
  • default-access (預設為 property): 指定 Hibernate 的預設的屬性通路政策。預設值為 property, 即使用 getter, setter 方法來通路屬性。若指定 access, 則 Hibernate 會忽略 getter/setter 方法, 而通過反射通路成員變量。
  • default-lazy(預設為 true): 設定 Hibernat morning的延遲加載政策. 該屬性的預設值為 true, 即啟用延遲加載政策. 若配置 Java 屬性映射, 集合映射時沒有指定 lazy 屬性, 則 Hibernate 将采用此處指定的延遲加載政策 。
  • auto-import (預設為 true): 指定是否可以在查詢語言中使用非全限定的類名(僅限于本映射檔案中的類)。
  • package (可選): 指定一個包字首,如果在映射文檔中沒有指定全限定的類名, 就使用這個作為包名。

【2】 class節點

class節點屬性如下:

Hibernate - 對象關系映射檔案(*.hbm.xml)詳解

class 元素用于指定類和表的映射

  • name:指定該持久化類映射的持久化類的類名。
  • table:指定該持久化類映射的表名, Hibernate 預設以持久化類的類名作為表名。
  • dynamic-insert: 若設定為 true, 表示當儲存一個對象時, 會動态生成 insert 語句, insert 語句中僅包含所有取值不為 null 的字段. 預設值為 false。
  • dynamic-update: 若設定為 true, 表示當更新一個對象時, 會動态生成 update 語句, update 語句中僅包含所有取值需要更新的字段。預設值為 false。
  • select-before-update:設定 Hibernate 在更新某個持久化對象之前是否需要先執行一次查詢,預設值為 false。
  • batch-size:指定根據 OID 來抓取執行個體時每批抓取的執行個體數。
  • lazy: 指定是否使用延遲加載,預設為true。
  • mutable: 若設定為 true, 等價于所有的​

    ​<property>​

    ​ 元素的 update 屬性為 false, 表示整個執行個體不能被更新。 預設為 true。
  • discriminator-value: 指定區分不同子類的值。當使用​

    ​<subclass/>​

    ​元素來定義持久化類的繼承關系時需要使用該屬性。

【3】 映射對象辨別符

Hibernate 使用對象辨別符(OID) 來建立記憶體中的對象和資料庫表中記錄的對應關系。 對象的 OID 和資料表的主鍵對應。 Hibernate 通過辨別符生成器來為主鍵指派。

Hibernate 推薦在資料表中使用代理主鍵, 即不具備業務含義的字段。代理主鍵通常為整數類型, 因為整數類型比字元串類型要節省更多的資料庫空間。

在對象-關系映射檔案中,​

​<id>​

​​元素用來設定對象辨別符. ​

​<generator>​

​子元素用來設定辨別符生成器。

① id節點設定持久化類的 OID 和表的主鍵的映射

  • name: 辨別持久化類 OID 的屬性名。
  • column: 設定辨別屬性所映射的資料表的列名(主鍵字段的名字)。
  • unsaved-value:若設定了該屬性, Hibernate 會通過比較持久化類的 OID 值和該屬性值來區分目前持久化類的對象是否為臨時對象。
  • type:指定 Hibernate 映射類型。

Hibernate 映射類型是 Java 類型與 SQL 類型的橋梁. 如果沒有為某個屬性顯式設定映射類型, Hibernate 會運用反射機制先識别出持久化類的特定屬性的 Java 類型, 然後自動使用與之對應的預設的 Hibernate 映射類型。

Java 的基本資料類型和包裝類型對應相同的 Hibernate 映射類型. 基本資料類型無法表達 null, 是以對于持久化類的 OID 推薦使用包裝類型。

② generator節點設定持久化類辨別符生成器

  • class: 指定使用的辨別符生成器全限定類名或其縮寫名

Hibernate 提供了辨別符生成器接口: IdentifierGenerator, 并提供了各種内置實作。

③ 幾種常見的Hibernate 主鍵生成政策

辨別符生成器 描述
increment 适用于代理主鍵。由Hibernate自動以遞增方式生成。用于為long, short或者int類型生成唯一辨別。隻有在沒有其他程序往同一張表中插進資料時才能使用。在叢集下不要使用。
identity 适用于代理主鍵。由底層資料庫生成辨別符。對DB2,MySQL, MS SQL Server, Sybase和HypersonicSQL的内置辨別字段提供支援。傳回的辨別符是long, short 或者int類型的。
sequence 适用于代理主鍵。Hibernate根據底層資料庫的序列生成辨別符,這要求底層資料庫支援序列。在DB2,PostgreSQL, Oracle, SAP DB, McKoi中使用序列(sequence),而在Interbase中使用天生器(generator)。傳回的辨別符是long, short或者 int類型的。
hilo 适用于代理主鍵。Hibernate分局high/low算法生成辨別符。使用一個高/低位算法來高效的生成long, short或者 int類型的辨別符。給定一個表和字段(預設分别是是hibernate_unique_key 和next_hi)作為高位值得來源。高/低位算法天生的辨別符隻在一個特定的資料庫中是唯一的。在使用JTA獲得的連接配接或者使用者自行提供的連接配接中,不要使用這種。
seqhilo 适用于代理主鍵。适用于代理主鍵。使用一個高/低位算法來高效的生成long, short或者 int類型的辨別符。
native 适用于代理主鍵。根據底層資料庫對自動生成辨別符的方式,自動選擇identity、sequence或hilo中的一個。
uuid.hex 适用于代理主鍵。Hibernate采用128位的UUID算法生成字元串類型的辨別符。在一個網絡中唯一(使用了IP位址)。UUID被編碼為一個32位16進制數字的字元串。
uuid.string 适用于代理主鍵。UUID被編碼成一個16字元長的字元串。使用了與上面一樣的UUID算法。UUID被編碼為一個16個字元長的任意ASCII字元組成的字元串。不能使用在PostgreSQL資料庫中。
assigned 适用于自然主鍵。由Java應用程式負責生成辨別符。讓應用程式在save()之前為對象配置設定一個标示符。
foreign 适用于代理主鍵。使用另外一個相關聯的對象的辨別符。
select hibernate3中新增的。需要提供一個唯一的辨別字段進行二次讀取,以擷取觸發器天生的主鍵值,通過param子元素進行定義。該方法主要針對遺留系統的改造工程,一些早期的系統主鍵依靠于觸發器天生。當資料庫insert時,觸發器捕捉這一操縱,并為主鍵指派,在插進資料庫後,再次讀取某一識别字段讀取已經插進的資料,擷取其主鍵值。

複合主鍵映射:

< composite-id >
< key-property ? column = "USERID" ? name = "userid" ? type = "java.lang.String" ></ key-property >
< key-property ? column = "WHEN" ? name = "when" ? type = "java.sql.Date" ></ key-property >
</ composite-id >      

複合主鍵的POJO類需要實作equals和hashcode方法,可以使用apache commons lang包中的工具類實作(commons-lang.jar)。

④ increment 辨別符生成器

increment 辨別符生成器由 Hibernate 以遞增的方式為代理主鍵指派。

Hibernate 會先讀取 NEWS 表中的主鍵的最大值, 而接下來向 NEWS 表中插入記錄時, 就在 max(id) 的基礎上遞增, 增量為 1.

适用範圍:

  • 由于 increment 生存辨別符機制不依賴于底層資料庫系統, 是以它适合所有的資料庫系統;
  • 适用于隻有單個 Hibernate 應用程序通路同一個資料庫的場合, 在叢集環境下不推薦使用它;
  • OID 必須為 long, int 或 short 類型, 如果把 OID 定義為 byte 類型, 在運作時會抛出異常。

⑤ identity 辨別符生成器

identity 辨別符生成器由底層資料庫來負責生成辨別符, 它要求底層資料庫把主鍵定義為自動增長字段類型。

适用範圍:

  • 由于 identity 生成辨別符的機制依賴于底層資料庫系統, 是以, 要求底層資料庫系統必須支援自動增長字段類型. 支援自動增長字段類型的資料庫包括: DB2, Mysql, MSSQLServer, Sybase 等
  • OID 必須為 long, int 或 short 類型, 如果把 OID 定義為 byte 類型, 在運作時會抛出異常

⑥ sequence 辨別符生成器

sequence 辨別符生成器利用底層資料庫提供的序列來生成辨別符。

<id name="id">
  <generator class="sequence" />
    <param name="sequence">news_seq</param>
  </generator >
</id>      

Hibernate 在持久化一個 News 對象時, 先從底層資料庫的 news_seq 序列中獲得一個唯一的辨別号, 再把它作為主鍵值。

适用範圍:

  • 由于 sequence 生成辨別符的機制依賴于底層資料庫系統的序列, 是以, 要求底層資料庫系統必須支援序列。 支援序列的資料庫包括: DB2, Oracle 等
  • OID 必須為 long, int 或 short 類型, 如果把 OID 定義為 byte 類型, 在運作時會抛出異常

⑦ hilo 辨別符生成器

hilo 辨別符生成器由 Hibernate 按照一種 high/low 算法*生成辨別符, 它從資料庫的特定表的字段中擷取 high 值.

<id name="id">
  <generator class="hilo" />
      <param name="table">HI_TABLE</param>
      <param name="column">NEXT_VALUE</param>
      <param name="max_lo">10</param>
  </generator >
</id>      

Hibernate 在持久化一個 News 對象時, 由 Hibernate 負責生成主鍵值. hilo 辨別符生成器在生成辨別符時, 需要讀取并修改 HI_TABLE 表中的 NEXT_VALUE 值。

适用範圍:

  • 由于 hilo 生存辨別符機制不依賴于底層資料庫系統, 是以它适合所有的資料庫系統;
  • OID 必須為 long, int 或 short 類型, 如果把 OID 定義為 byte 類型, 在運作時會抛出異常。

hilo 和 seqhilo生成器給出了兩種hi/lo算法的實作。

第一種情況:

<id name="id" type="id" column="id">
  <generator class="hilo">
    <param name="table">zhxy_hilo_tbl</param>
    <param name="column">next_value</param>
    <param name="max_lo">100</param>
  </generator>
</id>      

第二種情況:

<id name="id" type="long" column="cat_id">
  <generator class="seqhilo">
    <param name="sequence">hi_value</param>
    <param name="max_lo">100</param>
  </generator>
</id>      

第二種情況需要sequence的支援,這裡隻讨論更通用的第一種情況。

預設請況下使用的表是​

​hibernate_unique_key​

​​,預設字段叫作​

​next_hi​

​。next_hi必須有一條記錄否則會出現錯誤。

幾個簡寫解釋:

  • hi:高值-從資料庫取得的那個值
  • lo:低值-hibernate自動維護,取值1到max_low
  • max_low:映射檔案中配置的那個值。

那hibernate怎樣生成主鍵呢?

1.從資料庫中取得hi值,資料庫的next_value值加1;

2.hibernate取得lo值(0到max_lo-1循環,lo到max_lo時,執行步驟1,然後lo繼續從0到max_lo-1循環)

根據下面的公式計算值:​

​hi*(max_lo+1)+lo​

​​;

例如hi初始為2,max_lo為3。生成的值依次是:

讀取hi為2,寫到資料庫為3;

2*(3+1)+0=8;2*(3+1)+1=9;2*(3+1)+2=10;2*(3+1)+3=11;

這有次讀寫表zhxy_hilo_tbl操作,hi變為3,資料庫成為4;

3*(3+1)+0=12;3*(3+1)+1=13;

關閉資料庫,下次打開時,讀取hi值為4,資料庫變為5;4*(3+1)+0=16;

但是有一種特殊情況,就是hi是0的時候,那麼第一個值不是0*(max_lo+1)+0=0;而是跳過0,直接就是1 。

⑧ native辨別符生成器

native 辨別符生成器依據底層資料庫對自動生成辨別符的支援能力, 來選擇使用 identity, sequence 或 hilo 辨別符生成器。

适用範圍:

  • 由于 native 能根據底層資料庫系統的類型, 自動選擇合适的辨別符生成器, 是以很适合于跨資料庫平台開發;
  • OID 必須為 long, int 或 short 類型, 如果把 OID 定義為 byte 類型, 在運作時會抛出異常。

【4】property節點

property 元素用于指定類的屬性和表的字段的映射。

  • name:指定該持久化類的屬性的名字;
  • column:指定與類的屬性映射的表的字段名. 如果沒有設定該屬性, Hibernate 将直接使用類的屬性名作為字段名;
  • type:指定 Hibernate 映射類型。
Hibernate 映射類型是 Java 類型與 SQL 類型的橋梁. 如果沒有為某個屬性顯式設定映射類型, Hibernate 會運用反射機制先識别出持久化類的特定屬性的 Java 類型, 然後自動使用與之對應的預設的 Hibernate 映射類型.
  • not-null:若該屬性值為 true, 表明不允許為 null, 預設為 false
  • access:指定 Hibernate 的預設的屬性通路政策。預設值為 property, 即使用 getter, setter 方法來通路屬性. 若指定 field, 則 Hibernate 會忽略getter/setter方法, 而通過反射通路成員變量
  • unique: 設定是否為該屬性所映射的資料列添加唯一限制.
  • index: 指定一個字元串的索引名稱. 當系統需要 Hibernate 自動建表時, 用于為該屬性所映射的資料列建立索引, 進而加快該資料列的查詢.
  • length: 指定該屬性所映射資料列的字段的長度
  • scale: 指定該屬性所映射資料列的小數位數, 對 double, float, decimal 等類型的資料列有效.
  • formula:設定一個 SQL 表達式, Hibernate 将根據它來計算出派生屬性的值.
  • 派生屬性: 并不是持久化類的所有屬性都直接和表的字段比對, 持久化類的有些屬性的值必須在運作時通過計算才能得出來, 這種屬性稱為派生屬性。

使用 formula 屬性時

  • 派生屬性一般不會持久化到資料庫;
  • formula=“(sql)” 的英文括号不能少;
  • Sql 表達式中的列名和表名都應該和資料庫對應, 而不是和持久化對象的屬性對應;
  • 如果需要在 formula 屬性中使用參數, 這直接使用 where cur.id=id 形式, 其中 id 就是參數, 和目前持久化對象的 id 屬性對應的列的 id 值将作為參數傳入。

執行個體如下(News.hbm.xml):

//原先普通映射字段
<!--         <property name="describle" type="java.lang.String"> -->
<!--             <column name="DESCRIBLE" default="null" /> -->
<!--         </property> -->
<!-- 映射派生屬性 -->
<property name="describle" 
  formula="(SELECT concat(author, ': ', title) FROM NEWS n WHERE n.id = id)">
</property>      

測試代碼如下:

@Test
public void testFormula(){
  News news = (News) session.get(News.class, 1);
  news.setTitle("aaaa"); 
  
  System.out.println(news.getDescrible());
}      

測試結果如下:

Hibernate: 
    select
        news0_.ID as ID1_0_0_,
        news0_.TITLE as TITLE2_0_0_,
        news0_.AUTHOR as AUTHOR3_0_0_,
        news0_.DATE as DATE4_0_0_,
        news0_.CONTENT as CONTENT5_0_0_,
        news0_.PICTURE as PICTURE6_0_0_,
        (SELECT
            concat(news0_.author,
            ': ',
            news0_.title) 
        FROM
            NEWS n 
        WHERE
            n.id = news0_.id) as formula0_0_ 
    from
        NEWS news0_ 
    where
        news0_.ID=?
//列印的news.getDescrible()
AA: CC      

【5】映射組成關系與component

① 映射組成關系

建立域模型和關系資料模型有着不同的出發點:

  • 域模型: 由程式代碼組成, 通過細化持久化類的的粒度可提高代碼的可重用性, 簡化程式設計。
  • 在沒有資料備援的情況下, 應該盡可能減少表的數目, 簡化表之間的參照關系, 以便提高資料的通路速度。

Hibernate 把持久化類的屬性分為兩種:

  • 值(value)類型: 沒有 OID, 不能被單獨持久化, 生命周期依賴于所屬的持久化類的對象的生命周期。
  • 實體(entity)類型: 有 OID, 可以被單獨持久化, 有獨立的生命周期。
Hibernate - 對象關系映射檔案(*.hbm.xml)詳解

如上圖所示顯然無法直接用 property 映射 值類型 屬性。

② component元件

Hibernate 使用 ​

​<component>​

​元素來映射組成關系, 該元素表明pay 屬性是 Worker 類一個組成部分, 在 Hibernate 中稱之為元件。

​<component>​

​元素來映射組成關系

  • class:設定組成關系屬性的類型, 此處表明 pay 屬性為 Pay 類型;

​<parent>​

​元素指定元件屬性所屬的整體類

  • name: 整體類在元件類中的屬性名

Work類如下:

public class Worker {
  
  private Integer id;
  private Integer age;
  private String name;
  
  private Pay pay;
  
  public Integer getAge() {
    return age;
  }
  
  public void setAge(Integer age) {
    this.age = age;
  }

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Pay getPay() {
    return pay;
  }

  public void setPay(Pay pay) {
    this.pay = pay;
  }
  //...
}      

Pay類如下:

public class Pay {
  
  private int monthlyPay;
  private int yearPay;
  private int vocationWithPay;
  
  private Worker worker;
  
  public Worker getWorker() {
    return worker;
  }
  public void setWorker(Worker worker) {
    this.worker = worker;
  }
  public int getMonthlyPay() {
    return monthlyPay;
  }
  public void setMonthlyPay(int monthlyPay) {
    this.monthlyPay = monthlyPay;
  }
  public int getYearPay() {
    return yearPay;
  }
  public void setYearPay(int yearPay) {
    this.yearPay = yearPay;
  }
  public int getVocationWithPay() {
    return vocationWithPay;
  }
  public void setVocationWithPay(int vocationWithPay) {
    this.vocationWithPay = vocationWithPay;
  }
  //...
}      

Worker.hbm.xml如下:

<hibernate-mapping package="com.jane.model">
    <class name="Worker" table="WORKER">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="age" type="java.lang.Integer">
            <column name="AGE" />
        </property>
        
        <!-- 映射組成關系 -->
        <component name="pay" class="Pay">
            <parent name="worker"/>
            <!-- 指定組成關系的元件的屬性 -->
            <property name="monthlyPay" column="MONTHLY_PAY"></property>
            <property name="yearPay" column="YEAR_PAY"></property>
            <property name="vocationWithPay" column="VOCATION_WITH_PAY"></property>
        </component>
    </class>
</hibernate-mapping>      

測試代碼如下:

@Test
  public void testComponent(){
    Worker worker = new Worker();
    Pay pay = new Pay();
    
    pay.setMonthlyPay(1000);
    pay.setYearPay(80000); 
    pay.setVocationWithPay(5);
    
    worker.setAge(30);
    worker.setName("ABCD");
    worker.setPay(pay);
    
    session.save(worker);
    System.out.println(worker);
  }      

測試結果如下圖:

Hibernate - 對象關系映射檔案(*.hbm.xml)詳解

資料表如下:

Hibernate - 對象關系映射檔案(*.hbm.xml)詳解

man-to-one使用參考博文:單向多對一關系映射;

one-to-many使用參考博文:​單向一對多關系映射;

set節點使用參考博文:​雙向多對一關系映射;