上篇我們介紹了關聯映射的幾種形式,有單向多對一,單向一對多,還有雙向一對多。本篇接着介紹有關關聯映射的其他幾種映射方式,主要有以下幾種:
- 基於外鍵的單向一對一關聯映射
- 基於主鍵的單向一對一關聯映射
- 單向多對多關聯映射
一、基於外鍵的單向一對一關聯映射
具有一對一關聯的表結構也是很常見的,比如:一個人對應於一張身份證。於是我們的person表會有一個外鍵關聯到 idcard表的主鍵,只要這個外鍵列唯一即可保證person到idcard表的關係由多對一變爲一對一,也就是說單向的一對一關聯映射其實上也就是外鍵列唯一的多對一的關聯映射。例如:
public class Person {
private int id;
private String name;
private IdCard idcard;
//省略get,set方法
}
public class IdCard {
private int id;
private String code;
//省略get,set方法
}
<class name="Person" table="person">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="idcard" column="idcard_id" unique="true"/>
</class>
<class name="IdCard" table="idcard">
<id name="id">
<generator class="native"/>
</id>
<property name="code"/>
</class>
我們可以看到,幾乎所有的代碼和多對一關聯映射都是差不多的,只不過在 many-to-one 元素中指定 unique=”true”,原來可以有多個具有相同外鍵值的記錄映射到一的一端,現在指定外鍵值唯一之後,產生了唯一的一對一的關聯映射。下面看看我們的測試代碼:
//main 方法
Person p1 = new Person();
p1.setName("single");
IdCard idCard = new IdCard();
idCard.setCode("2345");
p1.setIdcard(idCard);
session.save(p1);
session.save(idCard);
我們分別創建兩端的一個實例對象,然後由person端維護這種關聯關係並將數據插入到數據庫。看一眼結果:
這就是基於外鍵的單向一對一關聯映射,與多對一的映射的唯一區別就在於,通過指定外鍵列唯一來讓多的一端唯一,從而形成這種一對一的映射關係。
二、基於主鍵的單向一對一關聯映射
所謂基於主鍵的映射就是指,其中的一張表的主鍵值依賴於另一張表的主鍵值。還是我們的人和身份證模型:
像這種IDCard表完全可以作爲屬性字段追加到person表的後面的情況(主鍵重複可覆蓋),我們叫這種關聯映射爲基於主鍵的一對一關聯映射。看看配置文件:
/*person實體類的映射文件的配置*/
<class name="Person" table="person">
<id name="id" column="pId">
<generator class="foreign">
<param name="property">idCard</param>
</generator>
</id>
<property name="name"/>
<one-to-one name="idCard" constrained="true"/>
</class>
/*IDCard實體類的映射文件的配置*/
<class name="IdCard" table="idCard">
<id name="id">
<generator class="native"/>
</id>
<property name="code"/>
</class>
可以看到,主要的變化還是在於實體類person。首先我們指定他的主鍵不再自增,而是由外鍵約束到其他表,對應的其他表的類型則是自己實體類的idCard屬性對應的表。one-to-one標籤則配置了person類中的IdCard屬性,便於我們在取數據的時候Hibernate填充數據到該屬性中。下面我們添加數據到數據庫中,通過查看輸出的Sql語句瞭解Hibernate是如何爲我們創建關聯的。
Person person = new Person();
person.setName("single");
IdCard idCard = new IdCard();
idCard.setCode("23456789");
person.setIdCard(idCard);
session.save(idCard);
session.save(person);
顯然,我們對於idcard表的主鍵指定了native自增,而對於person表的主鍵並沒有指定自增,person表的主鍵依賴於idcard的主鍵。我們並不用指定person表的主鍵值,因爲person會根據和自己關聯的idcard表的主鍵來爲自己的主鍵賦值。
三、單向多對多關聯映射
多對多的表關聯類型也是非常常見的,例如:
很常見的一個例子,一個學生可以有多個老師,同時一個老師也可以有多個學生,那麼這就是很明顯的多對多的關聯映射。針對這種情況,一般來說數據庫的表可以設計如下:
但是這種的表結構設計將會直接導致student表大量冗餘,雖然解決了這種多對多的表級關聯,但是存在大量冗餘。Hibernate中處理這種多對多的關聯關係是通過引入另一張表來實現對兩個表主鍵的關聯進而關聯了兩張表。
Student表和teacher表之間的多對多關聯完全由connect表進行體現,各自表中數據不再大量冗餘,這纔是一種比較清晰的表結構設計。下面我們看代碼:
public class Student {
private int id;
private String sName;
private Set<Teacher> teachers = new HashSet<Teacher>(0);
//省略get,set方法
}
public class Teacher {
private int id;
private String tName;
//省略get,set方法
}
這是兩張表對應的實體類,下面我們主要看映射文件的配置。
/*配置實體類Student*/
<class name="Student" table="student">
<id name="id">
<generator class="native"/>
</id>
<property name="sName"/>
<set name="teachers" table="connect" cascade="save-update">
<key column="stuId"></key>
<many-to-many class="Teacher" column="tId"/>
</set>
</class>
/*配置實體類Teacher*/
<class name="Teacher" table="teacher">
<id name="id">
<generator class="native"/>
</id>
<property name="tName"/>
</class>
Teacher實體類的配置並沒有什麼特殊需要解釋的地方,而對於Student實體類的配置卻稍有變化。其中的set標籤就負責創建一張新表並負責關聯兩張表,table屬性就是用於連接兩張表主鍵值的表名(connect)。對於用於連接的表,有兩個字段,一個是Student表的id,一個是teacher表的id,那麼key標籤就用於指定Student表id對應connect表中的名稱,teacher表的id對應到connect表的名稱則由many-to-many標籤的column屬性指定。下面我們插入多條數據:
顯然,通過抽出connect表連關聯兩張表,對於Student和teacher表的表結構來說是更加簡潔清晰的。
至此,有關Hibernate中關聯映射的內容已經簡單介紹完畢,雖然以後會更多的使用註解來配置這些映射關聯,但是都是基於XML的,對於新手來說,學習XML配置關聯映射是有助於理解註解配置。總結不到之處,望指出。