一、繼承映射
繼承是面向對象很重要的特性,它實現了代碼的服用,在關系模型中同樣也有繼承關系,這種繼承關系其實可以看做是一種枚舉關系,一種類型中可以枚舉出很多子類型,這些子類型和父對象形成了繼承關系,能夠對其進行枚舉的大部分都可以看做是一種繼承映射,所以這種枚舉關系可以看做是繼承映射,例如動物就是一種抽象類,它是其它動物豬、貓等的父類,它們之間就是一種繼承關系,如下圖:
這種繼承映射在轉化為關系模型后會生成一張表,那么這張表是如何區分這兩種類型的呢?用的是關系字段,需要在表中添加類型字段,使用關鍵字來標明對象的類型。所以上圖中的對象模型對應的表結構如下:
在生成表結構時,需要添加對應的字段類型,所以需要在映射文件中添加對應的映射鑒別器,這里就需要使用discriminator-value屬性。
1.類文件
類文件中沒有需要注意的地方,在編寫時注意之間的繼承關系即可。
清單一:Animal類代碼,只需要添加基本的屬性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.src.hibernate; public class Animal { //id號 private int id; public int getId() { return id; } public void setId( int id) { this .id = id; } //名稱 private String name; public String getName() { return name; } public void setName(String name) { this .name = name; } //性別 private boolean sex; public boolean isSex() { return sex; } public void setSex( boolean sex) { this .sex = sex; } } |
清單二:Bird和Pig類,添加基本的屬性,并繼承Animal類。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package com.src.hibernate; public class Bird extends Animal { //高度 private int height; public int getHeight() { return height; } public void setHeight( int height) { this .height = height; } } package com.src.hibernate; public class Pig extends Animal { //重量 private int weight; public int getWeight() { return weight; } public void setWeight( int weight) { this .weight = weight; } } |
2.映射文件
映射文件中需要添加對應的映射,該模型中只需要添加一個映射文件,因為只生成一張表,在映射文件中添加對應的子類映射,使用<subclass>標簽,標簽中添加鑒別器discriminator-value,該鑒別器屬性指明了在數據庫中寫入數據時指示寫入的是何種類型,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<? xml version = "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> < hibernate-mapping > < class name = "com.src.hibernate.Animal" table = "t_animal" > < id name = "id" > < generator class = "native" /> </ id > <!-- 加入鑒別標簽,且必須放在id后面 --> < discriminator column = "type" /> < property name = "name" /> < property name = "sex" type = "boolean" /> < subclass name = "com.src.hibernate.Pig" discriminator-value = "P" > < property name = "weight" /> </ subclass > < subclass name = "com.src.hibernate.Bird" discriminator-value = "B" > < property name = "height" /> </ subclass > </ class > </ hibernate-mapping > |
3.分析結果
生成的MySQL數據庫表中不僅會添加Animal的基本屬性,而且會添加Pig和Bird的屬性,因為在映射文件中使用<subclass>寫出了所添加的屬性,另外還添加了相應的鑒別器屬性,所以在數據庫中會添加對應的鑒別列,生成的表結構如下圖:
二、數據操作
1.寫入數據
在進行數據讀取和寫入操作時需要注意類中的操作使用了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public void testSave(){ Session session= null ; try { //創建session對象 session=HibernateUtils.getSession(); //開啟事務 session.beginTransaction(); Pig pig= new Pig(); pig.setName( "小豬豬" ); pig.setSex( true ); pig.setWeight( 200 ); session.save(pig); Bird bird= new Bird(); bird.setName( "xiaoniaoniao" ); bird.setSex( true ); bird.setHeight( 100 ); session.save(bird); session.getTransaction().commit(); } catch (Exception e){ e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } } |
2.多態查詢--get和hql
基本的查詢方法,只需要使用load和get方法即可,這里重點討論多態查詢。多態查詢是指Hibernate在加載對象時能夠采用instanceof鑒別出其真正的類型的對象即可為多態查詢。
Note:多態查詢不支持延遲加載,也就是說如果使用load方法,需要在映射文件中將延遲加載設置為false。
3.load延遲加載
load支持延遲加載,在加載對象時其實生成的是對象的代理,所以在使用多態查詢時需要在映射文件中將延遲加載設置為false,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<? xml version = "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> < hibernate-mapping > < class name = "com.src.hibernate.Animal" table = "t_animal" lazy = "false" > < id name = "id" > < generator class = "native" /> </ id > <!-- 加入鑒別標簽,且必須放在id后面 --> < discriminator column = "type" /> < property name = "name" /> < property name = "sex" type = "boolean" /> < subclass name = "com.src.hibernate.Pig" discriminator-value = "P" > < property name = "weight" /> </ subclass > < subclass name = "com.src.hibernate.Bird" discriminator-value = "B" > < property name = "height" /> </ subclass > </ class > </ hibernate-mapping > |
load加載方法,使用load加載該示例中支持多態查詢,在配置文件中將延遲加載設置為false,所以這里使用load方法能夠加載獲得相應的對象類。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public void testLoad(){ Session session= null ; try { session=HibernateUtils.getSession(); session.beginTransaction(); Animal ani=(Animal)session.load(Animal. class , 1 ); System.out.println(ani.getName()); //因為load默認支持lazy,所以我們看到的是Animal的代理 //所以采用了instanceof無法鑒別出真正的類型Pig //所以load在此情況下是不支持多態查詢的 if (ani instanceof Pig){ System.out.println( "我是小豬豬!" ); } else { System.out.println( "我不是小豬豬!" ); } session.getTransaction().commit(); } catch (Exception e){ e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } } |
4.hql查詢
hql支持多態查詢,這主要由于查詢出的是一個真正的對象,并不會返回一個代理,所以hql支持多態查詢,另外在查詢時需要注意查詢語句中不要使用表名,而是要使用類名稱,Hibernate會根據類名稱將其映射為對應的表名稱,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public void testLoad5(){ Session session= null ; try { session=HibernateUtils.getSession(); session.beginTransaction(); List<Animal> list=session.createQuery( "from Animal" ).list(); for (Iterator iter=list.iterator();iter.hasNext();){ Animal a=(Animal)iter.next(); if (a instanceof Pig){ System.out.println( "我是小豬豬!" ); } else { System.out.println( "我不是小豬豬!" ); } } session.getTransaction().commit(); } catch (Exception e){ e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } } |
查詢結果:
1
2
3
4
5
|
Hibernate: select animal0_.id as id0_, animal0_.name as name0_, animal0_.sex as sex0_, animal0_.weight as weight0_, animal0_.height as height0_, animal0_.type as type0_ from t_animal animal0_ 我是小豬豬! 我不是小豬豬! 我是小豬豬! 我不是小豬豬! |
三、繼承映射三種策略
1. 每個類分層結構一張表(Table per class hierarchy)
假設我們有接口Payment和它的幾個實現類: CreditCardPayment, CashPayment, 和ChequePayment。則“每個類分層結構一張表”(Table per class hierarchy)的映射代碼如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
< class name = "Payment" table = "PAYMENT" > < id name = "id" type = "long" column = "PAYMENT_ID" > < generator class = "native" /> </ id > < discriminator column = "PAYMENT_TYPE" type = "string" /> < property name = "amount" column = "AMOUNT" /> ... < subclass name = "CreditCardPayment" discriminator-value = "CREDIT" > < property name = "creditCardType" column = "CCTYPE" /> ... </ subclass > < subclass name = "CashPayment" discriminator-value = "CASH" > ... </ subclass > < subclass name = "ChequePayment" discriminator-value = "CHEQUE" > ... </ subclass > </ class > |
采用這種策略只需要一張表即可。它有一個很大的限制:要求那些由子類定義的字段, 如CCTYPE,不能有非空(NOT NULL)約束。
2. 每個子類一張表(Table per subclass)
對于上例中的幾個類而言,采用“每個子類一張表”的映射策略,代碼如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
< class name = "Payment" table = "PAYMENT" > < id name = "id" type = "long" column = "PAYMENT_ID" > < generator class = "native" /> </ id > < property name = "amount" column = "AMOUNT" /> ... < joined-subclass name = "CreditCardPayment" table = "CREDIT_PAYMENT" > < key column = "PAYMENT_ID" /> ... </ joined-subclass > < joined-subclass name = "CashPayment" table = "CASH_PAYMENT" > < key column = "PAYMENT_ID" /> < property name = "creditCardType" column = "CCTYPE" /> ... </ joined-subclass > < joined-subclass name = "ChequePayment" table = "CHEQUE_PAYMENT" > < key column = "PAYMENT_ID" /> ... </ joined-subclass > </ class > |
需要四張表。三個子類表通過主鍵關聯到超類表(因而關系模型實際上是一對一關聯)。
3. 每個子類一張表(Table per subclass),使用辨別標志(Discriminator)
注意,對“每個子類一張表”的映射策略,Hibernate的實現不需要辨別字段,而其他 的對象/關系映射工具使用了一種不同于Hibernate的實現方法,該方法要求在超類 表中有一個類型辨別字段(type discriminator column)。Hibernate采用的方法更 難實現,但從關系(數據庫)這點上來看,按理說它更正確。若你愿意使用帶有辨別字 段的“每個子類一張表”的策略,你可以結合使用<subclass> 與<join>,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
< class name = "Payment" table = "PAYMENT" > < id name = "id" type = "long" column = "PAYMENT_ID" > < generator class = "native" /> </ id > < discriminator column = "PAYMENT_TYPE" type = "string" /> < property name = "amount" column = "AMOUNT" /> ... < subclass name = "CreditCardPayment" discriminator-value = "CREDIT" > < join table = "CREDIT_PAYMENT" > < property name = "creditCardType" column = "CCTYPE" /> ... </ join > </ subclass > < subclass name = "CashPayment" discriminator-value = "CASH" > < join table = "CASH_PAYMENT" > ... </ join > </ subclass > < subclass name = "ChequePayment" discriminator-value = "CHEQUE" > < join table = "CHEQUE_PAYMENT" fetch = "select" > ... </ join > </ subclass > </ class > |
可選的聲明fetch="select",是用來告訴Hibernate,在查詢超類時, 不要使用外部連接(outer join)來抓取子類ChequePayment的數據。