ITEEDU

Hibernate Gossip: 多对一

一个实体简单的说就是在数据库中拥有一个表格,并拥有自已的数据库识别(Database identity)。

一个简单的实体与实体间之关系为多对一的关系,例如在学校宿舍中,使用者与房间的关系就是多对一的关系,多个使用者可以居住于一个房间。
多对一

如上图所示的,可以藉由room_id让使用者与房间产生关联,您可以如下建立user与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类别:
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 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=?