Hibernate的映射关联关系
1. 映射关联
l 多对一关联
l 一对一关联
l 一对多关联
l 双向一对多/多对一关联
l 多对多关联
2. 集合映射
l <set>,<list>,<map>,<bag>,<array>,<primitive-array>
3. Component组件映射
4. Hibernate中的继承、多态结构的映射
l Table per Class hierarchy 每个类分层结构一张表
l Table per subclass 每个子类一张表
l Table per concrete class 每个具体类一张表
l 映射策略之间的比较
一个实体简单的说就是在数据库中拥有一个表格,并拥有自已的数据库识别(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 ''
);
Hibernate的映射关联关系-多对一
用程序来表示的话,首先看看User类别: User.java
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
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
<hibernate-mapping>
<class name=“com.cstp.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
<hibernate-mapping>
<class name=“com.cstp.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=“com.cstp.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(“tiantian");
user1.setRoom(room1);
User user2 = new User();
user2.setName(“hhp");
user2.setRoom(room1);
User user3 = new User();
user3.setName(“zhangy");
user3.setRoom(room2);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(user1); // 主控方操作,被控方也会对应操作
session.save(user2);
session.save(user3);
tx.commit();
session.close();
怎么查询,例子如下:
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并没有完全实现以上的概念,它让使用者自行决定自动持久化的方式,当对象之间被指定关联(例如多对一、一对多等),您可以决定被持久化对象关联的暂存对象是否进行自动持久化与如何自动持久化。 在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(“tiantian");
user1.setRoom(room1);
User user2 = new User();
user2.setName(“hhp");
user2.setRoom(room1);
User user3 = new User();
user3.setName(“zhangy");
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
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;
}
}