通過 POJO 類的數據庫映射文件,Hibernate可以理解持久化類和數據表之間的對應關係,也可以理解持久化類屬性與數據庫表列之間的對應關係。
在運行時 Hibernate 將根據這個映射文件來生成各種 SQL 語句。
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 (可選): 指定一個包前綴,如果在映射文檔中沒有指定全限定的類名, 就使用這個作爲包名。
根元素的子節點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: 指定是否使用延遲加載
mutable: 若設置爲 true, 等價於所有的 <property> 元素的 update 屬性爲 false, 表示整個實例不能被更新. 默認爲 true
discriminator-value: 指定區分不同子類的值. 當使用 <subclass/> 元素來定義持久化類的繼承關係時需要使用該屬性
映射對象標示符(主鍵)
Hibernate 使用對象標識符(OID) 來建立內存中的對象和數據庫表中記錄的對應關係。對象的 OID 和數據表的主鍵對應. Hibernate 通過標識符生成器來爲 OID 賦值。
Hibernate 推薦在數據表中使用代理主鍵, 即不具備業務含義的字段.。代理主鍵通常爲整數類型, 因爲整數類型比字符串類型要節省更多的數據庫空間。
在對象-關係映射文件中, <id> 元素用來設置對象標識符. <generator> 子元素用來設定標識符生成器.
id:設定持久化類的 OID 和表的主鍵的映射,其屬性如下:
name: 標識持久化類 OID 的屬性名
column: 設置標識屬性所映射的數據列的列名(主鍵字段的名字).
unsaved-value:若設定了該屬性, Hibernate 會通過比較持久化類的 OID 值和該屬性值來區分當前持久化類的對象是否爲臨時對象
type:指定 Hibernate 映射類型. Hibernate 映射類型是 Java 類型與 SQL 類型的橋樑. 如果沒有爲某個屬性顯式設定映射類型, Hibernate 會運用反射機制先識別出持久化類
的特定屬性的 Java 類型, 然後自動使用與之對應的默認的 Hibernate 映射類型Java 的基本數據類型和包裝類型對應相同的 Hibernate 映射類型. 基本數據類型無法表達
null, 所以對於持久化類的 OID 推薦使用包裝類型
映射組成關係
Hibernate 把持久化類的屬性分爲兩種:
①值(value)類型:沒有 OID, 不能被單獨持久化, 生命週期依賴於所屬的持久化類的對象的生命週期。
②實體(entity)類型: 有 OID, 可以被單獨持久化, 有獨立的生命週期。
組成關係理解:實體類型的持久化類中包含有值類型的持久化類型屬性。
譬如:Employee類中不僅有Integer類型id、String類型的員工name、int類型的員工age等屬性,還有一個工資屬性,工資屬性的類型是Pay,Pay是另外一個實體類,
Employee與Pay之間就可以稱之爲組成關係。那麼要映射這種關係,<property/>標籤是不行的,hibernate中提供了<component/>標籤來映射這種關係。
<property name="age" column="AGE" type="int" length="3"/>
<!-- 映射組成關係 -->
<component name="pay" class="Pay">
<property name="monthPay" column="MONTH_PAY"/>
<property name="yearPay" column="YEAR_PAY" />
<property name="vocationWithPay" column="VOCATION_WITH_PAY"/>
</component>
</class>
映射多對一關聯關係
單向 N - 1
單向 n-1 關聯只需從 n 的一端可以訪問 1 的一端
域模型:從 Order 到 Customer 的多對一單向關聯需要在Order 類中定義一個 Customer 屬性, 而在 Customer 類中無需定義存放 Order 對象的集合屬性。
關係數據模型:ORDERS 表中的 CUSTOMER_ID 參照 CUSTOMER 表的主鍵。
Hibernate 使用 <many-to-one> 元素來映射多對一關聯關係。
<many-to-one name="customer" class="Customer" column="CUSTOMER_ID" />
映射流程理解
在映射單向多對一的關聯關係時,只需在多的一端做如下操作即可:
1、在其持久化類中添加聲明類型爲另一端持久化類的屬性,併爲其提供get、set方法。private Customer customer;
2、在其持久化類映射文件中使用<many-to-one>標籤映射上述屬性即可。
<many-to-one name="customer" class="Customer" column="CUST_ID" />
其中name屬性用來指定被映射的屬性名稱,class屬性用來標識該屬性的持久化類名, column屬性用來指定本映射文件對應數據表中外鍵列的名稱,
引用的是當前被映射屬性對應持久化類對應數據表的主鍵作爲外鍵。 其中class屬性可以省略不寫,如果省略了,hibernate會通過反射找到name屬性值對應持久化類 的名稱,
因爲多執行了一系列代碼,所以會影響hibernate的整體執行效率,所以建議不要省略。 不僅是class屬性,在配置文件中的所有屬性儘量不要省略,原因和class屬性一樣。
注意:保存的時候,要先保存 ‘一’ 端,再保存N端,這樣可以減少一次update操作
雙向 1 - N
雙向 1-n 需要在 1 的一端可以訪問 n 的一端, 反之依然。
域模型:從 Order 到 Customer 的多對一單向關聯需要在Order 類中定義一個 Customer 屬性,而在 Customer 類中需定義存放 Order 對象的集合屬性。
關係數據模型:ORDERS 表中的 CUSTOMER_ID 參照 CUSTOMER 表的主鍵。
當 Session 從數據庫中加載 Java 集合時,創建的是 Hibernate 內置集合類的實例, 因此在持久化類中定義集合屬性時必須把屬性聲明爲 Java 接口類型。
Hibernate 的內置集合類具有集合代理功能,支持延遲檢索策略。
事實上, Hibernate 的內置集合類都封裝了 JDK 中的集合類,這使得 Hibernate 能夠對緩存中的集合對象進行髒檢查,按照集合對象的狀態來同步更新數據庫。
在定義集合屬性時,通常把它初始化爲集合實現類的一個實例。這樣可以提高程序的健壯性,避免應用程序訪問取值爲 null 的集合的方法而拋出 NullPointerException。
private Set<Order> orders = new HashSet<Order>(); get/set方法。
Hibernate 使用 <set> 元素來映射 set 類型的屬性
<!-- 映射一對多關聯關係 -->
set類型屬性 對應數據表名稱 放棄主動維護表關係
<set name="orders" table="ORDER_TABLE" inverse="true">
數據表關聯列名稱
<key><column name="CUSTOMER_ID"></column></key>
屬性對應持久化類
<one-to-many class="Order"/>
</set>
<set>元素的name屬性用來設定待映射的持久化類的屬性。
<key> 元素設定與所關聯的持久化類對應的表的外鍵,其column屬性用來指定關聯表的外鍵名。
<one-to-many> 元素設定集合屬性中所關聯的持久化類,其 class屬性用來指定關聯的持久化類的類名。
<set> 元素的 inverse 屬性
在hibernate中通過對 inverse 屬性來決定是由雙向關聯的哪一方來維護表和表之間的關係。 inverse = false 的爲主動方,inverse = true 的爲被動方, 由主動方負責維護關聯關
系。在沒有設置 inverse=true 的情況下,兩邊都維護表和表之間關係,即默認情況。
在 1 - N 關係中,將 n 方設爲主控方將有助於性能改善(譬如校長記住所有學生的名字,不太可能,但要讓所有學生記住校長的名字,就容易的多)。
在 1 - N 關係中,若將 1 方設爲主控方會額外多出 update 語句。
插入數據時無法同時插入外鍵列,因而無法爲外鍵列添加非空約束。
<set> 元素有一個 order-by 屬性, 如果設置了該屬性, 當 Hibernate 通過 select 語句到數據庫中檢索集合對象時, 利用 order by 子句進行排序。
<set> 元素的 cascade 屬性:
在對象 – 關係映射文件中, 用於映射持久化類之間關聯關係的元素,<set>、<many-to-one> 和 <one-to-one> 都有一個 cascade 屬性,,
它用於指定如何操縱與當前對象關聯的其他對象。
對於映射雙向多對一關聯關係的理解
在映射雙向一對多的關聯關係時,只需在單向的基礎上添加如下操作即可:
1、在'一'這一端的持久化類中添加另一端持久化類的Set<E>集合屬性,併爲其提供get、set方法。private Set<Order> orders = new HashSet<Order>();
2、在'一'這一端的持久化類映射文件中使用<set>標籤映射該屬性。
<set name="orders" table="ORDER_TABLE" inverse="true">
<key column="CUST_ID"></key>
<one-to-many class="Order"/>
</set>
其中name屬性用來指定被映射的屬性名稱,table屬性用來指定被映射屬性變化時將反映或者作用到表的名稱,
inverse="true"表示'一'這一端放棄主動維護表關係,用來提高hibernate效率。
<key column="CUST_ID"></key>用來設定與所關聯的持久化類對應數據表的外鍵,column屬性用來指定外鍵的名稱。
該名稱與另一端映射文件中<many-to-one>標籤的column屬性值保持一致,<many-to-one name="customer" class="Customer" column="CUST_ID" />
映射一對一關聯關係
對於使用外鍵的方式映射一對一關聯關係的理解
在映射一對一關聯關係時,如果採用外鍵的方式進行映射,需做如下操作:
1、在兩端持久化類中分別添加對方類型的關聯屬性,併爲其提供get、set方法。
Department類:private Manager manager;
Manager類:private Manager department;
2、選擇任意一端存放外鍵,在存放外鍵的這一端使用<many-to-one>標籤映射屬性,並添加unique="true"屬性來表示爲 1-1 關聯。
<many-to-one name="department" class="Department" column="DEPT_ID" unique="true"/>
其中,name屬性用來指定關聯屬性名稱,class屬性指定關聯屬性對應的持久化類名,
column屬性指定關聯外鍵的名稱。引用的是class屬性指定持久化類對應數據表的主鍵作爲外鍵。
3、在不存放外鍵的一端使用one-to-one元素映射屬性,該元素使用 property-ref 屬性指定使用
被關聯實體主鍵以外的字段作爲關聯字段(查詢時關聯表之間關聯時使用的關聯鍵)。
<one-to-one name="manager" class="Manager" property-ref="department"/> 其中,name屬性用來指定關聯屬性名稱,class屬性指定關聯屬性對應的持久化類名。
基於主鍵的映射策略:指一端的主鍵生成器使用 foreign 策略,表明根據“對方”的主鍵來生成自己的主鍵,自己並不能獨立生成主鍵.。
<param> 子元素指定當前持久化類的那個屬性作爲 “對方”。
採用foreign主鍵生成器策略的一端增加 one-to-one元素映射關聯屬性,其 one-to-one 屬性還應增加 constrained=“true” 屬性(用來生成外鍵約束);
另一端增加 one-to-one 元素來映射關聯屬性。<one-to-one name="dept" class="Department" ></one-to-one>
constrained(約束):指定爲當前持久化類對應的數據庫表的主鍵添加一個外鍵約束,引用被關聯的對象(“對方”)所對應的數據庫表主鍵。
<one-to-one name="mgr" class="Manager" constrained="true"></one-to-one>
對於使用主鍵方式映射一對一關聯關係的理解
在映射一對一關聯關係時,如果採用主鍵的方式進行映射,需做如下操作:
1、在兩端持久化類中分別添加對方類型的關聯屬性,併爲其提供get、set方法。
Department類:private Manager manager;
Manager類:private Manager department;
2、選擇任意一端的主鍵生成策略採用foreign,在這端使用<one-to-one>標籤映射屬性(Manager.class),併爲其添加constrained="true"屬性,用來爲該主鍵生成外鍵約束。
<id name="managerId" column="MANAGER_ID">
<generator class="foreign">
<param name="property">department</param>
</generator>
</id>
其中<param>子元素用來指定當前持久化類的哪個屬性作爲對方。hibernate通過反射找到其對應持久化類對應數據表的主鍵作爲當前持久化類對應數據表的主鍵。
<one-to-one name="department" class="Department" constrained="true"/>其中,name屬性用來指定關聯屬性名稱,class屬性指定關聯屬性對應的持久化類名,
constrained屬性用來通知hibernate爲當前映射文件對應的持久化類對應的數據表主鍵添加外鍵約束,引用的是class屬性指定持久化類對應數據表的主鍵作爲其外鍵。
3、在主鍵不採用foreign生成策略的一端使用one-to-one元素映射屬性(Department.class)
<one-to-one name="manager" class="Manager" ></one-to-one> 其中,name屬性用來指定關聯屬性名稱,class屬性指定關聯屬性對應的持久化類名。
映射多對多關聯關係
單向 N-N
n-n 的關聯必須使用連接表(中間表)
與 1-n 映射類似,必須爲 set 集合元素添加 key 子元素,指定 CATEGORIES_ITEMS 表中參照 CATEGORIES 表的外鍵爲 CATEGORIY_ID。 與 1-n 關聯映射不同的是,
建立 n-n 關聯時, 集合中的元素使用 many-to-many. many-to-many 子元素的 class 屬性指定 items 集合中存放的是 Item 對象,column 屬性指定 CATEGORIES_ITEMS 表
中參照 ITEMS 表的外鍵爲 ITEM_ID。
對映射單向多對多關聯關係的理解
在映射單向多對多的關聯關係時,必須使用連接表(中間表),需做如下配置:
1、在其中一端添加Set<E>集合屬性,併爲其提供get、set方法,其中E是另一端持久化類。private Set<Item> items = new HashSet<Item>();
2、在添加集合屬性類對應的映射文件中,使用<set>元素映射上述集合屬性。
<set name="items" table="CATEGORY_ITEM_TABLE" order-by="ITEM_ID">
<key column="CATE_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</set>
其中name屬性用來指定被映射的屬性名稱,table屬性用來指定中間表的名稱,
<key column="CATE_ID"></key>用來設定中間表參照本映射文件對應數據表的外鍵作爲主鍵,column屬性用來指定中間表主鍵的名稱。
<many-to-many class="Item" column="ITEM_ID"/>用來標識中間表參照class屬性值指定的持久化類對應數據表的外鍵作爲主鍵,並用column屬性值作爲主鍵名稱。
class屬性值用來指定<set>元素的name屬性指定的集合對象中存放的對象類型。
雙向多對多
在映射雙向多對多的關聯關係時,必須使用連接表(中間表),在單向多對多的基礎上需做如下配置:
1、在沒有Set屬性對象的一端添加Set<E>集合屬性,併爲其提供get、set方法,其中E是另一端持久化類。 private Set<Category> categories = new HashSet<Category>();
2、在添加集合屬性類對應的映射文件中,使用<set>元素映射上述集合屬性。
<set name="categories" table="CATEGORY_ITEM_TABLE" inverse="true">
<key column="ITEM_ID"></key>
<many-to-many class="Category" column="CATE_ID"/>
</set>
其中name屬性用來指定被映射的屬性名稱,table屬性用來指定中間表的名稱,
<key column="ITEM_ID"></key>用來設定中間表參照本映射文件對應數據表的外鍵作爲主鍵, column屬性用來指定中間表主鍵的名稱。
<many-to-many class="Category" column="CATE_ID"/>
用來標識中間表參照class屬性值指定的持久化類對應數據表的外鍵作爲主鍵,並用column屬性值作爲主鍵名稱。
class屬性值用來指定<set>元素的name屬性指定的集合對象中存放的對象類型。
注意:
1、在雙向 多對多關聯的兩邊都需指定連接表的表名及外鍵列的列名。兩個集合元素 set 的 table 元素的值必須指定,而且必須相同(同一張中間表當然要相同了)。
set元素的兩個子元素:key 和 many-to-many 都必須指定 column 屬性,其中,key 和 many-to-many 分別指定本持久化類和關聯類在連接表中的外鍵列名,
因此兩邊的 key 與 many-to-many 的column屬性交叉相同。也就是說,一邊的set元素的key的 cloumn值爲a, many-to-many 的 column 爲b;
則另一邊的 set 元素的 key 的 column 值 b,many-to-many的 column 值爲 a.
2、對於雙向 n-n 關聯,須把其中一端的 inverse 設置爲 true,否則可能會造成主鍵衝突。
映射繼承關聯關係(Hibernate支持三種繼承映射策略)
1、<subclass>標籤的方式
採用 subclass 的繼承映射可以實現對於繼承關係中父類和子類使用同一張表。
因爲父類和子類的實例全部保存在同一個表中,因此需要在該表內增加一列,
使用該列來區分每行記錄到低是哪個類的實例----這個列被稱爲辨別者列(discriminator)。
在這種映射策略下,使用 subclass 來映射子類,使用 class 或 subclass 的 discriminator-value 屬性指定辨別者列的值
所有子類定義的字段都不能有非空約束。如果爲那些字段添加非空約束,那麼父類的實例在那些列其實並沒有值,
這將引起數據庫完整性衝突,導致父類的實例無法保存到數據庫中。
若採用subclass的方式映射繼承關係,則需做如下配置:
1、只需爲父類添加對應的hbm.xml文件,使用<subclass>標籤映射子類,其配置如下:
<class name="Person" table="Person_TABLE" discriminator-value="PERSON">
<id name="personId" column="PERSION_ID">
<generator class="native"/>
</id>
<!-- 添加辨別者列,最好放到id元素下 -->
<discriminator column="TYPE_VALUE"/>
<property name="name" column="NAME" length="10"/>
<subclass name="Student" discriminator-value="STUDENT">
<property name="school" column="SCHOOL" length="30"/>
</subclass>
</class>
其中,<discriminator column="TYPE_VALUE"/>用來添加辨別者列,column屬性指定其列名。
discriminator-value="STUDENT"屬性用來指定辨別者列的值。
2、<joined-subclass>標籤的方式
採用 joined-subclass 元素的繼承映射可以實現每個子類一張表。
採用這種映射策略時,父類實例保存在父類表中,子類實例由父類表和子類表共同存儲。因爲子類實例也是一個特殊的父類實例,因此必然也包含了父類實例的屬性。
於是將子類和父類共有的屬性保存在父類表中,子類增加的屬性,則保存在子類表中。
在這種映射策略下,無須使用鑑別者列,但需要爲每個子類使用 key 元素映射共有主鍵,該主鍵必須與父類標識屬性的列名相同。但如果繼承樹的深度很深,可能查詢一個子
類實例時,需要跨越多個表,因爲子類的數據一次保存在多個父類中。子類增加的屬性可以添加非空約束。因爲子類的屬性和父類的屬性沒有保存在同一個表中。
若採用joined-subclass的方式映射繼承關係,則需做如下配置:
1、只需爲父類添加對應的hbm.xml文件,使用<joined-subclass>標籤映射子類,其配置如下:
<class name="Person" table="Person_TABLE">
<id name="personId" column="PERSION_ID">
<generator class="native"/>
</id>
<property name="name" column="NAME" length="10"/>
<joined-subclass name="Student" table="STUDENT_TABLE">
<!-- 映射和父類共有的主鍵 -->
<key column="PER_ID"/>
<property name="school" column="SCHOOL" length="30"/>
</joined-subclass>
</class>
其中,<key column="PER_ID"/>用來標識使用父類表的主鍵作爲子類表的主鍵,column屬性指定子類主鍵列名。
3、<union-subclass>標籤的方式(推薦方式)
採用 union-subclass 元素可以實現將每一個實體對象映射到一個獨立的表中。
子類增加的屬性可以有非空約束 -- 即父類實例的數據保存在父表中,而子類實例的數據保存在子類表中。
子類實例的數據僅保存在子類表中, 而在父類表中沒有任何記錄。
在這種映射策略下,子類表的字段會比父類表的映射字段要多,因爲子類表的字段等於父類表的字段、加子類增加屬性的總和。
在這種映射策略下,既不需要使用鑑別者列,也無須使用 key 元素來映射共有主鍵。使用 union-subclass 映射策略是不可使用 identity 的主鍵生成策略,
因爲同一類繼承層次中所有實體類都需要使用同一個主鍵種子,
即多個持久化實體對應的記錄的主鍵應該是連續的。 受此影響,也不該使用 native 主鍵生成策略, 因爲 native 會根據數據庫來選擇使用 identity 或 sequence。
對不能使用identity主鍵生成策略的理解:
子類和父類只有一個hbm.xml映射文件,hibernate的查詢可能依賴此文件,只是猜測,有待考證。
若採用union-subclass的方式映射繼承關係,則需做如下配置:
1、只需爲父類添加對應的hbm.xml文件,使用<union-subclass>標籤映射子類,其配置如下:
<class name="Person" table="Person_TABLE">
<id name="personId" column="PERSION_ID">
<generator class="hilo"/>
</id>
<property name="name" column="NAME" length="10"/>
<union-subclass name="Student" table="STUDENT_TABLE">
<!-- 直接映射屬性 -->
<property name="school" column="SCHOOL" length="30"/>
</union-subclass>
</class>
其中,<discriminator column="TYPE_VALUE"/>用來添加辨別者列,column屬性指定其列名。
discriminator-value="STUDENT"屬性用來指定辨別者列的值。
三種方式的比較
待續。。