一個實體簡單的說就是在數據庫中擁有一個表格,並擁有自已的數據庫識別(Database identity)。 java
一個簡單的實體與實體間之關係爲多對一的關係,例如在學校宿舍中,使用者與房間的關係就是多對一的關係,多個使用者能夠居住於一個房間。 mysql
如上圖所示的,能夠藉由room_id讓使用者與房間產生關聯,您能夠以下創建user與room表格: sql
CREATE TABLE user ( 數據庫
id INT(11) NOT NULL auto_increment PRIMARY KEY, session
name VARCHAR(100) NOT NULL default '', app
room_id INT(11) ide
); this
CREATE TABLE room ( spa
id INT(11) NOT NULL auto_increment PRIMARY KEY, .net
address VARCHAR(100) NOT NULL default ''
);
用程序來表示的話,首先看看User類別:
User.java
package onlyfun.caterpillar;
public class User {
private Integer id;
private String name;
private Room room;
public User() {
}
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 Room getRoom() {
return room;
}
public void setRoom(Room room) {
this.room = room;
}
}
User類別中有一room屬性,將參考至Room實例,多個User實例可共同參考一個Room實例,Room類別設計以下:
Room.java
package onlyfun.caterpillar;
public class Room {
private Integer id;
private String address;
public Room() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
在映射文件方面,先來看看Room.hbm.xml:
Room.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.Room" table="room">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address"
column="address"
type="java.lang.String"/>
</class>
</hibernate-mapping>
沒什麼,很簡單的一個映射文件,而在User.hbm.xml中,使用<many-to-one>標籤來映像多對一關係:
User.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="room"
column="room_id"
class="onlyfun.caterpillar.Room"
cascade="all"
outer-join="true"/>
</class>
</hibernate-mapping>
在<many-to-one>的設定中,cascade表示主控方(User)進行save-update、delete等相關操做時,被控方(Room)是否也一併進行相關操做,簡單的說,也就是您儲存或更新User實例時,當中的Room實例是否一併對數據庫發生儲存或操做,設定爲 all,表示主控方任何操做,被控方也進行對應操做。
注意到room_id 與 onlyfun.caterpillar.Room 對應 那麼room_id的值爲 Room的id;
一個儲存的例子以下:
Room room1 = new Room();
room1.setAddress("NTU-M8-419");
Room room2 = new Room();
room2.setAddress("NTU-G3-302");
User user1 = new User();
user1.setName("bush");
user1.setRoom(room1);
User user2 = new User();
user2.setName("caterpillar");
user2.setRoom(room1);
User user3 = new User();
user3.setName("momor");
user3.setRoom(room2);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(user1); // 主控方操做,被控方也會對應操做
session.save(user2);
session.save(user3);
tx.commit();
session.close();
數據庫中將儲存如下的內容:
mysql> select * from user;
+-------+-------------+-----------+
| id | name | room_id |
+-------+-------------+-----------+
| 1 | bush | 1 |
| 2 | caterpillar | 1 |
| 3 | momor | 2 |
+-------+-------------+-----------+
3 rows in set (0.00 sec)
mysql> select * from room;
+----+-------------------+
| id | address |
+----+-------------------+
| 1 | NTU-M8-419 |
| 2 | NTU-G3-302 |
+----+-------------------+
2 rows in set (0.00 sec)
在查詢時的例子以下:
Session session = sessionFactory.openSession();
User user = (User) session.load(User.class, new Integer(1));
System.out.println(user.getName());
System.out.println(user.getRoom().getAddress());
session.close();
在設定outer-join爲true的狀況下,Hibernate將使用如下的SQL一次查詢全部的數據:
Hibernate:
select user0_.id as id1_, user0_.name as name0_1_, user0_.room_id as room3_0_1_,
room1_.id as id0_, room1_.address as address1_0_ from user user0_
left outer join room room1_ on user0_.room_id=room1_.id where user0_.id=?
在不設定outer-join爲true的狀況下,Hibernate則使用如下的SQL分別查詢user與room表格:
Hibernate:
select user0_.id as id0_, user0_.name as name0_0_, user0_.room_id as room3_0_0_
from user user0_ where user0_.id=?
Hibernate:
select room0_.id as id0_, room0_.address as address1_0_
from room room0_ where room0_.id=?
在Java程序中,對象與對象之間會透過某些關係互相參考,若是有一個對象已是持久化對象,被它參考的對象直覺上也應該要持久化,以維持對象之間關聯的完整性,這是藉由可達性完成持久化(Persistence by reachability)的基本概念。
若是將對象之間的關聯想象爲一個樹形圖,從某一個持久化物件爲樹根出發,父節點如果持久化對象,則被父節點參考到的子節點應自動持久化,而另外一方面,若是有一子節點沒辦法藉由任何的父節點來參考至它,則它沒有被持久化的需求,它應從數據庫中加以刪除。
Hibernate並無徹底實現以上的概念,它讓使用者自行決定自動持久化的方式,當對象之間被指定關聯(例如多對1、一對多等),您能夠決定被持久化對象關聯的暫存對象是否進行自動持久化與如何自動持久化。
在Hibernate中是以映像文件中卷標上的cascade屬性來設定,預設上是none,以多對一中的範例來講,若是不設定cascade爲true,則您必須分別對User實例與Room實例進行儲存:
Room room1 = new Room();
room1.setAddress("NTU-M8-419");
Room room2 = new Room();
room2.setAddress("NTU-G3-302");
User user1 = new User();
user1.setName("bush");
user1.setRoom(room1);
User user2 = new User();
user2.setName("caterpillar");
user2.setRoom(room1);
User user3 = new User();
user3.setName("momor");
user3.setRoom(room2);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
// 儲存Room實例
session.save(room1);
session.save(room2);
// 儲存User實例
session.save(user1);
session.save(user2);
session.save(user3);
tx.commit();
session.close();
使用cascade自動持久化時,會先檢查被關聯對象的id屬性,未被持久化的對象是否儲存是藉由id屬性的unsaved-value決定,預設是 null,也就是沒有參考至任何值時儲存對象,若是您使用int、long這樣的原生型態(Primitive type)時,因爲數據成員的初始會被設定爲0,因此您必須自行指定默認值,例如(若是id的數據型態是long的話):
....
<id name="id" column="id" unsaved-value="0">
<generator class="native"/>
</id>
....
在多對一中,User對Room是多對一的關係,User實例維護着對Room實例的參考,若是將這個關係反過來,由Room實例維護對多個User實例的數據,就是一對多的關係。
具體來講,能夠設計User類別以下:
User.java
package onlyfun.caterpillar;
public class User {
private Integer id;
private String name;
public User() {
}
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;
}
}
而在Room類別中,使用Set來記錄多個User:
Room.java
package onlyfun.caterpillar;
import java.util.Set;
public class Room {
private Integer id;
private String address;
private Set users;
public Room() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Set getUsers() {
return users;
}
public void setUsers(Set users) {
this.users = users;
}
public void addUser(User user) {
users.add(user);
}
public void removeUser(User user) {
users.remove(user);
}
}
這種方式即所謂單向一對多關係,也就是Room實例知道User實例的存在,而User實例則沒有意識到Room實例。
(在多對一中,則是單向多對一關係,即User知道Room的存在,但Room不知道User的存在。)
在映射文件上,先來看看User.hbm.xml:
User.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
</class>
</hibernate-mapping>
在單向關係中,被參考的對象其映像文件就如單一實體同樣的配置,接下來看看Room.hbm.xml,使用<one-to-many>卷標配置一對多:
Room.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.Room" table="room">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address"
column="address"
type="java.lang.String"/>
<set name="users" table="user" cascade="all">
<key column="room_id"/>
<one-to-many class="onlyfun.caterpillar.User"/>
</set>
</class>
</hibernate-mapping>
接着您能夠以下儲存對象:
User user1 = new User();
user1.setName("bush");
User user2 = new User();
user2.setName("caterpillar");
User user3 = new User();
user3.setName("momor");
Room room1 = new Room();
room1.setUsers(new HashSet());
room1.setAddress("NTU-M8-419");
room1.addUser(user1);
room1.addUser(user2);
Room room2 = new Room();
room2.setUsers(new HashSet());
room2.setAddress("NTU-G3-302");
room2.addUser(user3);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(room1); // cascade 操做
session.save(room2);
tx.commit();
session.close();
數據庫中將儲存如下的表格:
mysql> select * from user;
+----+-------------+-----------+
| id | name | room_id |
+----+-------------+------------+
| 1 | bush | 1 |
| 2 | caterpillar | 1 |
| 3 | momor | 2 |
+----+-------------+------------+
3 rows in set (0.01 sec)
mysql> select * from room;
+----+------------------+
| id | address |
+----+------------------+
| 1 | NTU-M8-419 |
| 2 | NTU-G3-302 |
+----+------------------+
2 rows in set (0.00 sec)
在多對1、一對多中都是單向關聯,也就是其中一方關聯到另外一方,而另外一方不知道本身被關聯。
若是讓雙方都意識到另外一方的存在,這就造成了雙向關聯,在多對1、一對多的例子能夠改寫一下,從新設計User類別以下:
User.java
package onlyfun.caterpillar;
public class User {
private Integer id;
private String name;
private Room room;
public User() {
}
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 Room getRoom() {
return room;
}
public void setRoom(Room room) {
this.room = room;
}
}
Room類別以下:
Room.java
package onlyfun.caterpillar;
import java.util.Set;
public class Room {
private Integer id;
private String address;
private Set users;
public Room() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Set getUsers() {
return users;
}
public void setUsers(Set users) {
this.users = users;
}
public void addUser(User user) {
users.add(user);
}
public void removeUser(User user) {
users.remove(user);
}
}
如此,User實例可參考至Room實例而維持多對一關係,而Room實例記得User實例而維持一對多關係。
在映射文件方面,能夠以下撰寫:
User.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="room"
column="room_id"
class="onlyfun.caterpillar.Room"
cascade="save-update"
outer-join="true"/>
</class>
</hibernate-mapping>
Room.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.Room" table="room">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address"
column="address"
type="java.lang.String"/>
<set name="users" table="user" cascade="save-update">
<key column="room_id"/>
<one-to-many class="onlyfun.caterpillar.User"/>
</set>
</class>
</hibernate-mapping>
映像文件雙方都設定了cascade爲save-update,因此您能夠用多對一的方式來維持關聯:
User user1 = new User();
user1.setName("bush");
User user2 = new User();
user2.setName("caterpillar");
Room room1 = new Room();
room1.setAddress("NTU-M8-419");
user1.setRoom(room1);
user2.setRoom(room1);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(user1);
session.save(user2);
tx.commit();
session.close();
或是反過來由一對多的方式來維持關聯:
User user1 = new User();
user1.setName("bush");
User user2 = new User();
user2.setName("caterpillar");
Room room1 = new Room();
room1.setUsers(new HashSet());
room1.setAddress("NTU-M8-419");
room1.addUser(user1);
room1.addUser(user2);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(room1);
tx.commit();
session.close();
這邊有個效率議題能夠探討,上面的程序片斷Hibernate將使用如下的SQL進行儲存:
Hibernate: insert into room (address) values (?)
Hibernate: insert into user (name, room_id) values (?, ?)
Hibernate: insert into user (name, room_id) values (?, ?)
Hibernate: update user set room_id=? where id=?
Hibernate: update user set room_id=? where id=?
上面的程序寫法表示關聯由Room單方面維持,而主控方也是Room,User不知道Room的room_id是多少,因此必須分別儲存Room與User以後,再更新user的room_id。
在一對多、多對一造成雙向關聯的狀況下,能夠將關聯維持的控制權交給多的一方,這樣會比較有效率,理由不難理解,就像是在公司中,老闆要記住多個員工的姓名快,仍是每個員工都記得老闆的姓名快。
因此在一對多、多對一造成雙向關聯的狀況下,能夠在「一」的一方設定控制權反轉,也就是當儲存「一」的一方時,將關聯維持的控制權交給「多」的一方,以上面的例子來講,能夠設定Room.hbm.xml以下:
Room.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.Room" table="room">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address"
column="address"
type="java.lang.String"/>
<set name="users" table="user" cascade="save-update" inverse="true">
<key column="room_id"/>
<one-to-many class="onlyfun.caterpillar.User"/>
</set>
</class>
</hibernate-mapping>
因爲關聯的控制權交給「多」的一方了,因此直接儲存「一」方前,「多」的一方必須意識到「一」的存在,因此程序片斷必須改成以下:
User user1 = new User();
user1.setName("bush");
User user2 = new User();
user2.setName("caterpillar");
Room room1 = new Room();
room1.setUsers(new HashSet());
room1.setAddress("NTU-M8-419");
room1.addUser(user1);
room1.addUser(user2);
// 多方必須意識到單方的存在
user1.setRoom(room1);
user2.setRoom(room1);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(room1);
tx.commit();
session.close();
上面的程序片斷Hibernate將使用如下的SQL:
Hibernate: insert into room (address) values (?)
Hibernate: insert into user (name, room_id) values (?, ?)
Hibernate: insert into user (name, room_id) values (?, ?)
若是控制權交給另外一方了,而另外一方沒有意識到對方的存在的話會如何?試着將上面的程序片斷中user1.setRoom(room1);與user2.setRoom(room1);移去,執行以後,您會發現數據庫中room_id會出現null值,這種結果就比如在多對一中,您沒有分配給User一個Room,理所固然的,room_id會出現null。
如今考慮每個User配給一間Room,造成一對一,user表格透過room_id做爲外鍵參考至room:
在表格創建方面,使用多對一中的表格創建語句就能夠了:
CREATE TABLE user (
id INT(11) NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(100) NOT NULL default '',
room_id INT(11)
);
CREATE TABLE room (
id INT(11) NOT NULL auto_increment PRIMARY KEY,
address VARCHAR(100) NOT NULL default ''
);
對象方面,User的實例會參考至Room實例,而Room實例也參考至User實例:
User.java
package onlyfun.caterpillar;
public class User {
private Integer id;
private String name;
private Room room;
public User() {
}
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 Room getRoom() {
return room;
}
public void setRoom(Room room) {
this.room = room;
}
}
Room.java
package onlyfun.caterpillar;
public class Room {
private Integer id;
private String address;
private User user;
public Room() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
使用外鍵來完成一對一,其實就是限制多對一關係中,「多」的一方只能有一個參考至「一」的一方,也就是多對一關係的一個特例,這能夠在映像文件中使用<many-to-one>標籤時,加上"unique"屬性來設定,例如:
User.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="room"
column="room_id"
class="onlyfun.caterpillar.Room"
cascade="all"
outer-join="true"
unique="true"/>
</class>
</hibernate-mapping>
到這邊爲止,單向一對一的映像已經完成,若是要再完成雙向一對一的關係,則能夠在Room.hbm.xml中使用<one-to-one>標籤來定義:
Room.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.Room" table="room">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address"
column="address"
type="java.lang.String"/>
<one-to-one name="user"
class="onlyfun.caterpillar.User"
property-ref="room"/>
</class>
</hibernate-mapping>
在<one-to-one>中,property-ref告訴Hibernate,查詢出user並將其參考至room。
一個儲存的例子以下:
User user1 = new User();
user1.setName("bush");
Room room1 = new Room();
room1.setAddress("NTU-M8-419");
user1.setRoom(room1);
User user2 = new User();
user2.setName("caterpillar");
Room room2 = new Room();
room2.setAddress("NTU-M8-418");
user2.setRoom(room2);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(user1);
session.save(user2);
tx.commit();
session.close();
在查詢Room時,User也會一加載,例如:
Session session = sessionFactory.openSession();
Room room = (Room) session.load(Room.class, new Integer(23));
System.out.println(room.getUser().getName());
session.close();
上面的查詢程序,Hibernate將使用如下的SQL:
Hibernate:
select
room0_.id as id1_, room0_.address as address1_1_, user1_.id as id0_, user1_.name as name0_0_,
user1_.room_id as room3_0_0_
from room room0_ left outer join user user1_ on room0_.id=user1_.room_id where room0_.id=?
一對一關聯的另外一種方式,是限制兩個實體的主鍵必須一致,如此直接透過兩個表格的主鍵就可肯定一對一關聯,而不用額外的外鍵參考。
例如user與room表格,能夠以下創建:
CREATE TABLE user (
id INT(11) NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(100) NOT NULL default ''
);
CREATE TABLE room (
id INT(11) NOT NULL auto_increment PRIMARY KEY,
address VARCHAR(100) NOT NULL default ''
);
User類別與Room類別的設計使用一對一(惟一外鍵關聯)中的設計便可,接着在User.hbm.xml方面以下設計:
User.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<one-to-one name="room"
class="onlyfun.caterpillar.Room"
cascade="all"/>
</class>
</hibernate-mapping>
在Room.hbm.xml的設計方面以下:
Room.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.Room" table="room">
<id name="id" column="id">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="address"
column="address"
type="java.lang.String"/>
<one-to-one name="user"
class="onlyfun.caterpillar.User"
constrained="true"/>
</class>
</hibernate-mapping>
在Room的id主鍵上,使用foreign表示與外鍵共享主鍵,也就是與User實體共享主鍵,而constrained設定爲true,表示約束room的主鍵必須與user中對應數據的主鍵相同。
一個儲存的實例以下:
User user1 = new User();
user1.setName("bush");
Room room1 = new Room();
room1.setAddress("NTU-M8-419");
// 互相設定關聯
user1.setRoom(room1);
room1.setUser(user1);
User user2 = new User();
user2.setName("caterpillar");
Room room2 = new Room();
room2.setAddress("NTU-M8-418");
// 互相設定關聯
user2.setRoom(room2);
room2.setUser(user2);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(user1);
session.save(user2);
tx.commit();
session.close();
數據庫中將有如下的儲存結果:
mysql> select * from user;
+----+-------------+
| id | name |
+----+-------------+
| 1 | bush |
| 2 | caterpillar |
+----+-------------+
2 rows in set (0.00 sec)
mysql> select * from room;
+----+------------------+
| id | address |
+----+------------------+
| 1 | NTU-M8-419 |
| 2 | NTU-M8-418 |
+----+------------------+
2 rows in set (0.00 sec)
在數據庫表格上要進行多對多對應,能夠藉由一箇中介表格來完成,也就是藉由多對1、一對多來完成多對多關聯。
多對多因爲使用了中介表格,在查詢效率不彰,且在程序的對象模式上,多對多會使得對象與對象之間彼此依賴,並非一個很好的設計方式,在設計上應避免使用多對多關係。
若是必定要使用多對多關係的話,在表格上先以下創建:
CREATE TABLE user (
id INT(11) NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(100) NOT NULL default ''
);
CREATE TABLE user_server (
user_id INT(11),
server_id INT(11)
);
CREATE TABLE server (
id INT(11) NOT NULL auto_increment PRIMARY KEY,
address VARCHAR(100) NOT NULL default ''
);
先設計User類別以下:
User.java
package onlyfun.caterpillar;
import java.util.Set;
public class User {
private Integer id;
private String name;
private Set servers;
public User() {
}
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 Set getServers() {
return servers;
}
public void setServers(Set servers) {
this.servers = servers;
}
}
再來設計Server類別以下:
Server.java
package onlyfun.caterpillar;
import java.util.Set;
public class Server {
private Integer id;
private String address;
private Set users;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Set getUsers() {
return users;
}
public void setUsers(Set users) {
this.users = users;
}
}
在映像文件上,使用<many-to-many>標籤來完成映像關係:
User.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="user">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<set name="servers"
table="user_server"
cascade="save-update">
<key column="user_id"/>
<many-to-many class="onlyfun.caterpillar.Server"
column="server_id"/>
</set>
</class>
</hibernate-mapping>
注意到cascade是設定爲save-update,由於在多對多的關係中,不多由於刪除其中之一,而所關聯的實體都要一併刪除的,因此設定save-update,表示在save或update時,一併對關聯的對象進行對應的save或update。
Server.hbm.xml的定義以下:
Server.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.Server" table="server">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address" type="java.lang.String"/>
<set name="users"
table="user_server"
inverse="true"
cascade="save-update">
<key column="server_id"/>
<many-to-many class="onlyfun.caterpillar.User"
column="user_id"/>
</set>
</class>
</hibernate-mapping>
一個儲存時的例子以下:
Server server1 = new Server();
server1.setAddress("PC-219");
server1.setUsers(new HashSet());
Server server2 = new Server();
server2.setAddress("PC-220");
server2.setUsers(new HashSet());
Server server3 = new Server();
server3.setAddress("PC-221");
server3.setUsers(new HashSet());
User user1 = new User();
user1.setName("caterpillar");
user1.setServers(new HashSet());
User user2 = new User();
user2.setName("momor");
user2.setServers(new HashSet());
// 多對多,互相參考
user1.getServers().add(server1);
user1.getServers().add(server2);
user1.getServers().add(server3);
server1.getUsers().add(user1);
server2.getUsers().add(user1);
server3.getUsers().add(user1);
user2.getServers().add(server1);
user2.getServers().add(server3);
server1.getUsers().add(user2);
server3.getUsers().add(user2);
Session session = sessionFactory.openSession();
Transaction tx= session.beginTransaction();
session.save(user1);
session.save(user2);
tx.commit();
session.close();
執行後數據庫的內容以下:
mysql> select * from user;
+----+--------------+
| id | name |
+----+--------------+
| 1 | caterpillar |
| 2 | momor |
+----+--------------+
2 rows in set (0.00 sec)
mysql> select * from user_serv
+----------+-------------+
| user_id | server_id |
+----------+-------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
+----------+-------------+
5 rows in set (0.00 sec)
mysql> select * from server;
+----+-----------+
| id | address |
+----+-----------+
| 1 | PC-219 |
| 2 | PC-221 |
| 3 | PC-220 |
+----+-----------+
3 rows in set (0.00 sec)
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1351899