對象關系映射檔案,即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節點
屬性如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cGcq5CNwQTMyEjYyEGOihTZ3ATYyYzXzQDO0gTM4AzLcZDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.jpg)
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節點屬性如下:
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, 等價于所有的
元素的 update 屬性為 false, 表示整個執行個體不能被更新。 預設為 true。<property>
- 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, 可以被單獨持久化, 有獨立的生命周期。
如上圖所示顯然無法直接用 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);
}
測試結果如下圖:
資料表如下:
man-to-one使用參考博文:單向多對一關系映射;
one-to-many使用參考博文:單向一對多關系映射;
set節點使用參考博文:雙向多對一關系映射;