在關系型資料庫中,如果表與表之間存在多對多的關系,是不符合關系型資料庫設計範式的。一般需要把一個多對多關系映射為一張關聯雙方的表,這張表叫做連接配接表。
不管是單向映射還是雙向映射,多對多關系都是基于連接配接表實作的。
1、下面通過使用者(User)和角色(Role)來講解多對多的關聯映射。一個使用者(User)有多個角色(Role),一個角色對應多個使用者,是以使用者和角色之間構成多對多的關聯關系。
實體類User和Role的資料庫表設計如下:
CREATE TABLE user (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE role (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
連接配接表user_role可以有兩中建表方式:
第一種:
CREATE TABLE user_role (
user_id INT(11) NOT NULL,
role_id INT(11) NOT NULL,
foreign key(user_id) references user (id),
foreign key(role_id) references role (id)
);
第二種:
CREATE TABLE user_role (
user_id INT(11) NOT NULL,
role_id INT(11) NOT NULL,
foreign key(user_id) references user (id),
foreign key(role_id) references role (id),
primary key(user_id, role_id)
);
注意,建立連接配接表user_role時,不能另外建立一個主鍵字段。要麼像第一種方式其主鍵由資料庫自動生成,要麼像第二種方式将兩個外鍵結合聲明成一個複合主鍵。
1. 雙向的多對多關聯映射(User<--->Role)
實體類User:
public class User {
private int id;
private String name;
private Set roles = new HashSet();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Set getRoles() {
return roles;
}
public void setRoles(Set roles) {
this.roles = roles;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
實體類Role:
public class Role {
private int id;
private String name;
private Set users = new HashSet();
public Set getUsers() {
return users;
}
public void setUsers(Set users) {
this.users = users;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
實體類User的映射檔案:
<?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="User" table="user">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
<set name="roles" table=“user_role” cascade=“save-update” inverse = “false”>
<key column="user_id"></key>
<many-to-many class="Role" column="role_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
下面介紹下<set>元素中各屬性和子元素的含義。
name:說明在User類中,存放Role對象的集合屬性是roles。
table:說明在這個多對多關聯中,存儲關聯關系的連接配接表是user_role。
inverse:說明關聯關系的主要方,值為true或false,預設的inverse=“false”(當inverse省略時的值)。值為false的一端為主要方,來維護關聯雙方資料庫更新的同步。這個映射檔案中inverse的值為false,表明User為主導方,當User對象的資料庫表發生更新時,關聯的Role對象的資料庫表也随之更新。
cascade:說明級聯關系的模式,即操作一個對象時,是否同樣操作所關聯的對象。預設的cascade=“none”,cascade的取值可以為none、save-update、delete、all等。all:在所有的操作的情況下均進行級聯;none:在所有操作的情況下均不進行級聯操作;save-update:在執行更新操作時級聯;delete:在執行删除操作時級聯。inverse和cascade配合起來使用。
key:說明連接配接表user_role中使用外鍵user_id來關聯表user。
many-to-many:說明這個多對多關系中,另一個多方實體類是Role,并且使用連接配接表的role_id字段來關聯表role。
實體類Role的映射檔案:
<?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="Role" table="role">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
<set name="users" table="user_role">
<key column="role_id"></key>
<many-to-many class="User" column="user_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
2.單向的多對多關聯映射(User--->Role)
對于單向的多對多關聯映射,隻需把雙向關聯的一端去掉就得到單向的多對多關聯映射。對于從User到Role的單向關聯關系,将Role一端的關聯關系去掉即可,是以實體類User和它的映射檔案不變。
實體類Role:
public class Role {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
實體類Role的映射檔案:
<?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="Role" table="role">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
3. 雙向多對多關聯應該注意的事項
(1)在雙方的映射檔案中應該都設定cascade屬性為:cascade=“save-update”,這樣如果在存儲時沒有把關聯的另一方存入,Hibernate會進行級聯存儲,避免發生存儲臨時對象的錯誤。
(2)在雙方的映射檔案中,應該設定一方的inverse屬性為inverse=“true”,另一方為inverse=“false”,這樣資料庫隻根據一方的狀态存入關聯關系,避免重複存取關聯表。
(3)在程式中設定好雙方對象的關聯關系,這樣才能保證無論主要方是哪一方,它們的關聯關系都能存入資料庫。