"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate. sourceforge.net/hibernate-mapping-3.0.dtd">
可以看到,在两个实体映射文件中都配置了many-to-many关系,这样的配置已经可以通过任意一个多端来维护另一端。实现持久化的代码见例6.21。
例6.21:实现持久化
public void run() {
//创建Room实体
Room room = new Room();
//设置Room.name
room.setName("rwhome");
//设置Room.roomnumber
room.setRoomnumber("001");
//创建第二个Room实体
Room room2 = new Room();
//设置Room.name
room2.setName("rwhome2");
//设置Room.roomnumber
room2.setRoomnumber("002");
//创建UserInfo实体
UserInfo userInfo = new UserInfo();
//设置UserInfo.name
userInfo.setName("rw");
//设置UserInfo.sex
userInfo.setSex("M");
//创建第二个UserInfo实体
UserInfo userInfo2 = new UserInfo();
//设置UserInfo.name
userInfo2.setName("rw2");
//设置UserInfo.sex
userInfo2.setSex("M2");
//创建第二个UserInfo实体与第二个Room实体的关系,通过连接表维护关系
①Set roomSet = new HashSet();
roomSet.add(room2);
userInfo2.setRooms(roomSet);
//创建第一个Room实体与第一、第二个UserInfo实体的关系,通过连接表维护关系
//添加UserInfo实体到集合userInfoSet
②Set userInfoSet = new HashSet();
userInfoSet.add(userInfo);
userInfoSet.add(userInfo2);
room.setUsers(userInfoSet);
//创建Hibernate Session
Session session = HibernateSessionFactory.currentSession();
//启动事务
Transaction tx = session.beginTransaction();
//持久化Room实体
//此时将更新表room2和userinfo2以及维护关系的roomuser表
③session.save(room);
//提交事务
tx.commit();
//关闭Hibernate Session
HibernateSessionFactory.closeSession();
session = HibernateSessionFactory.currentSession();
tx = session.beginTransaction();
//获取Room实体以成为持久化状态,此时将获取room2表的所有字段信息
④Room roomF1 = (Room)session.get(Room.class, room.getId());
//通过外连接roomusers获取userinfo2表的对应字段信息
⑤Iterator iF1 = roomF1.getUsers().iterator();
while(iF1.hasNext()) {
UserInfo userInfoF = (UserInfo)iF1.next();
System.out.println(userInfoF.getId());
System.out.println(userInfoF.getName());
System.out.println(userInfoF.getSex());
}
//获取Room实体以成为持久化状态,此时将获取room2表的所有字段信息
⑥Room roomF2 = (Room)session.get(Room.class, room2.getId());
//通过外连接roomusers获取userinfo2表的对应字段信息
⑦Iterator iF2 = roomF2.getUsers().iterator();
while(iF2.hasNext()) {
UserInfo userInfoF = (UserInfo)iF2.next();
System.out.println(userInfoF.getId());
System.out.println(userInfoF.getName());
System.out.println(userInfoF.getSex());
}
tx.commit();
HibernateSessionFactory.closeSession();
}
针对该段代码的SQL语句如下:
Hibernate:
/* ③session.save(room)的动作,插入第一个Room实体 */
insert
into
room2
(NAME, roomnumber, id)
values
(?, ?, ?)
Hibernate:
/* ③session.save(room)的动作,通过②的动作,插入第二个UserInfo实体*/
insert
into
userinfo2
(NAME, SEX, id)
values
(?, ?, ?)
Hibernate:
/* ③session.save(room)的动作,通过①的动作,插入第二个Room实体*/
insert
into
room2
(NAME, roomnumber, id)
values
(?, ?, ?)
Hibernate:
/* ③session.save(room)的动作,通过②的动作,插入第一个UserInfo实体*/
insert
into
userinfo2
(NAME, SEX, id)
values
(?, ?, ?)
Hibernate:
/* ③session.save(room)的动作,通过②的动作,维护第一个Room实体和第一个UserInfo实体关系*/
insert
into
roomusers
(roomid, userid)
values
(?, ?)
Hibernate:
/* ③session.save(room)的动作,通过②的动作,维护第一个Room实体和第二个UserInfo实体关系*/
insert
into
roomusers
(roomid, userid)
values
(?, ?)
Hibernate:
/* ③session.save(room)的动作,通过②的动作,维护第二个UserInfo实体和第一个Room实体关系*/
insert
into
roomusers
(userid, roomid)
values
(?, ?)
Hibernate:
/* ④Room roomF1 = (Room)session.get(Room.class, room.getId())的动作,获取第一个Room实体 */
select
room0_.id as id2_0_,
room0_.NAME as NAME2_0_,
room0_.roomnumber as roomnumber2_0_
from
room2 room0_
where
room0_.id=?
Hibernate:
/* ⑤Iterator iF1 = roomF1.getUsers().iterator()的动作,通过连接表获取多个UserInfo实体,在本实现中对应将获取两条UserInfo实体记录 */
select
users0_.roomid as roomid1_,
users0_.userid as userid1_,
userinfo1_.id as id0_0_,
userinfo1_.NAME as NAME0_0_,
userinfo1_.SEX as SEX0_0_
from
roomusers users0_
left outer join
userinfo2 userinfo1_
on users0_.userid=userinfo1_.id
where
users0_.roomid=?
Hibernate:
/* ⑥Room roomF2 = (Room)session.get(Room.class, room2.getId())的动作,获取第二个Room实体 */
select
room0_.id as id2_0_,
room0_.NAME as NAME2_0_,
room0_.roomnumber as roomnumber2_0_
from
room2 room0_
where
room0_.id=?
Hibernate:
/* ⑦Iterator iF2 = roomF2.getUsers().iterator()的动作,通过连接表获取多个UserInfo实体,在本实现中对应只会获取一条UserInfo实体记录 */
select
users0_.roomid as roomid1_,
users0_.userid as userid1_,
userinfo1_.id as id0_0_,
userinfo1_.NAME as NAME0_0_,
userinfo1_.SEX as SEX0_0_
from
roomusers users0_
left outer join
userinfo2 userinfo1_
on users0_.userid=userinfo1_.id
where
users0_.roomid=?
通过本实现可以看到,对于多对多关系的维护在任意一个多端都可以进行。对于持久化方式的实现,不用考虑连接表,只对实体进行关系的维护可以直接影响连接表。
6.3.4 其他通过连接表的映射配置
在之前的小节中已经给出了一对多/多对一、多对多的两种映射配置和持久化实现。其实在Hibernate中,还支持更多的连接表关系映射。这里作一个总结并给出映射配置。
(1)一对多单向映射配置:与一对多/多对一双向关系配置类似,只是在多端不需要使用join元素来配置与一端的关联,见例6.22。
例6.22:连接表单向一对多的映射文件
Room2.hbm.xml(一端的配置)
UserInfo2.hbm.xml(多端的配置)
连接表的单向一对多关系只能通过一端来维护关系,而在抓取信息时,通过多端是无法抓取到一端信息的。在通过一端抓取多端记录时,将会执行外连接语句的SQL。连接表的单向一对多比非连接表的单向一对多建模语义要清晰得多,在效率上也不低,因此非常常见。
(2)多对一单向映射配置。与一对多/多对一双向关系配置类似,只是在一端不需要使用set元素来配置与多端的关联,见例6.23。
例6.23:连接表单向多对一的映射文件
Room2.hbm.xml(一端的配置)
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate. sourceforge.net/hibernate-mapping-3.0.dtd">
UserInfo2.hbm.xml(多端的配置)
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate. sourceforge.net/hibernate-mapping-3.0.dtd">
使用连接表的单向多对一关系是非常常见的,在通过多端抓取一端记录时,仅通过外连接连接表获取一端的主键或实体。
(3)连接表的单向一对一关系并不常见,只需在单向一对多的一端映射配置中设置属性unique即可,这里不做具体介绍。
(4)连接表的单向多对多关系也并不常见,这里仅给出其映射配置,见例6.24。
例6.24:连接表的单向多对多映射
Room2.hbm.xml(多端的配置)
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate. sourceforge.net/hibernate-mapping-3.0.dtd">
UserInfo2.hbm.xml(多端的配置)
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate. sourceforge.net/hibernate-mapping-3.0.dtd">
(5)连接表的双向一对一关系并不常见,其配置映射需要在两端都配置元素,见例6.25。
例6.25:连接表的双向一对一映射配置
Room2.hbm.xml(一端的配置)
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate. sourceforge.net/hibernate-mapping-3.0.dtd">
UserInfo2.hbm.xml(一端的配置)
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate. sourceforge.net/hibernate-mapping-3.0.dtd">
通过连接表来实现双向一对一关联并不是一个好办法,虽然Hibernate提供了这种方式,但是并不推荐使用。
6.3.5 结语
在Hibernate中提供了多种连接表关系映射的配置方案。其持久化实现并不用关心连接表,而是直接关注当前的实体,而框架在后台会根据配置自行对连接表进行维护来保证关系。因此在一定的条件下使用连接表的关系映射是非常有帮助的。下面是各种关系的一种比较。
1.实体一对多关系
(1)更新(获取)一端时同时更新(获取)多端,业务要求只通过一端来维护关系,使用双向一对多、单向一对多、连接表双向一对多、连接表单向一对多都可以,推荐使用双向一对多、连接表单向一对多。
(2)更新(获取)一端时同时更新(获取)多端,业务要求通过一端和多端都可维护关系,使用双向一对多、连接表双向一对多都可以,确定业务推荐双向一对多;扩展业务推荐连接表双向一对多。
2.实体多对多关系
(1)更新(获取)多端时同时更新(获取)另一多端,业务要求只通过一个多端来维护关系,使用连接表双向多对多、连接表单向多对多都可以,推荐使用连接表单向多对多。
(2)更新(获取)多端时同时更新(获取)另一多端,业务要求两个多端都可以维护关系,只能使用连接表双向多对多。
3.实体一对一关系
(1)更新(获取)一端时同时更新(获取)另一端,业务要求只通过一个一端来维护关系,使用双向一对一、单向一对一、连接表双向一对一、连接表单向一对一都可以,推荐使用单向一对一。
(2)更新(获取)一端时同时更新(获取)另一一端,业务要求两个一端都可维护关系,使用双向一对一、连接表双向一对一都可以,推荐使用双向一对一。