Hibernate3 实体之间的关系。

一對一(唯一外鍵關聯)

現在考慮每一個User配給一間Room,形成一對一,user表格透過room_id作為外鍵參考至room:

一對一

在表格建立方面,使用
多對一 中的表格建立語句就可以了:
    create table room (
        id bigint not null auto_increment,
        address varchar(255),
        primary key (id)
    )

    create table user (
        id bigint not null auto_increment,
        name varchar(255),
        room_id bigint unique,
        primary key (id)
    )


物件方面,User的實例會參考至Room實例,而Room實例也參考至User實例:

  • User.java
package onlyfun.caterpillar;

public class User {
private Long id;
private String name;
private Room room;

public User() {}

public Long getId() {
return id;
}

public void setId(Long 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 Long id;
private String address;
private User user;

public Room() {}

public Long getId() {
return id;
}

public void setId(Long 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">
<generator class="native"/>
</id>

<property name="name" column="name"/>

<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" />

<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 = HibernateUti.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
 
session.save(user1);
session.save(user2);
       
tx.commit();
session.close();

在查詢Room時,User也會一載入,例如:
Session session = HibernateUtil.getSessionFactory().openSession();
Room room = (Room) session.load(Room.class, new Long(23));
System.out.println(room.getUser().getName());
session.close();

上面的查詢程式,Hibernate將使用以下的SQL:
Hibernate:
    select
        room0_.id as id1_1_,
        room0_.address as address1_1_,
        user1_.id as id0_0_,
        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 room (
        id bigint not null,
        address varchar(255),
        primary key (id)
    )

    create table user (
        id bigint not null auto_increment,
        name varchar(255),
        primary key (id)
    )


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">
<generator class="native"/>
</id>

<property name="name" column="name"/>

<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" />

<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 = HibernateUtil.getSessionFactory().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)

多對一

一個實體簡單的說就是在資料庫中擁有一個表格,並擁有自已的資料庫識別(Database identity)。

一個簡單的實體與實體間之關係為多對一的關係,例如在學校宿舍中,使用者與房間的關係就是多對一的關係,多個使用者可以居住於一個房間。
多對一

如上圖所示的,可以藉由room_id讓使用者與房間產生關聯,您可以如下建立user與room表格:

create table room (
   id bigint not null auto_increment,
   address varchar(255),
   primary key (id)
)

create table user (
   id bigint not null auto_increment,
   name varchar(255),
   room_id bigint,
   primary key (id)
)


用程式來表示的話,首先看看User類別:

  • User.java
package onlyfun.caterpillar;

public class User {
private Long id;
private String name;
private Room room;

public User() {}

public Long getId() {
return id;
}

public void setId(Long 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 Long id;
private String address;

public Room() {}

public Long getId() {
return id;
}

public void setId(Long 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" />
</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">
<generator class="native"/>
</id>

<property name="name" column="name"/>

<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-pdate、delete等相關操作時,被控方(Room)是否也一併進行相關操作,簡單的說,也就是您儲存或更新User實例時,被參考到的Room實例是否一併對資料庫發生儲存或操作,設定為 all,表示主控方任何操作,被控方也進行對應操作。

一個儲存的例子如下:

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 = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
       
session.save(user1); // 主控方操作,被控方也會對應操作
session.save(user2);
session.save(user3);
       
tx.commit();
session.close();


關於cascade,可以進一步參考
cascade 的意義

資料庫中將儲存以下的內容:

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 = HibernateUtil.getSessionFactory().openSession();
User user = (User) session.load(User.class, new Long(1));
System.out.println(user.getName());
System.out.println(user.getRoom().getAddress());
session.close();


在設定outer-join為true的情況下,Hibernate將使用以下的SQL一次查詢所有的資料:

Hibernate:
    select
        user0_.id as id0_1_,
        user0_.name as name0_1_,
        user0_.room_id as room3_0_1_,
        room1_.id as id1_0_,
        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_0_,
        user0_.name as name0_0_,
        user0_.room_id as room3_0_0_
    from
        user user0_
    where
        user0_.id=?

Hibernate:
    select
        room0_.id as id1_0_,
        room0_.address as address1_0_
    from
        room room0_
    where
        room0_.id=?

 一對多

多對一 中,User對Room是多對一的關係,User實例維護著對Room實例的參考,如果將這個關係反過來,由Room實例維護對多個User實例的資料,就是一對多的關係。

具體來說,可以設計User類別如下:

  • User.java
package onlyfun.caterpillar;

public class User {
private Long id;
private String name;

public User() {}

public Long getId() {
return id;
}

public void setId(Long 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 Long id;
private String address;
private Set users;

public Room() {}

public Long getId() {
return id;
}

public void setId(Long 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">
<generator class="native"/>
</id>

<property name="name" column="name"/>

</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 = HibernateUtil.getSessionFactory().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)


關於一對多還有效能方面的一些議題,可以參考 雙向關聯(inverse 的意義)

多對多

在資料庫表格上要進行多對多對應,可以藉由一個中介表格來完成,也就是藉由多對一、一對多來完成多對多關聯。
多對多

多對多由於使用了中介表格,在查詢效率不彰,且在程式的物件模式上,多對多會使得物件與物件之間彼此依賴,並不是一個很好的設計方式,在設計上應避免使用多對多關係。

如果一定要使用多對多關係的話,在表格上先如下建立:
    create table server (
        id integer not null auto_increment,
        address varchar(255),
        primary key (id)
    )

    create table user (
        id integer not null auto_increment,
        name varchar(255),
        primary key (id)
    )

    create table user_server (
        user_id integer not null,
        server_id integer not null,
        primary key (user_id, server_id)
    )


先設計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">
<generator class="native"/>
</id>

<property name="name" column="name"/>

<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"/>

<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 = HibernateUtil.getSessionFactory().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)


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值