上篇学习了Hibernate的基本映射,也就是单表映射,很容易就能理解,但是对于关系数据库来说,表之间存在关系是比不可少的,关系数据库中存在的关系是通过主外键建立起来的,反应到Hibernate要如何通过对象表现呢?下面我们就继续学习Hibernate的对象关系映射。
我们先从最常见的多对一和一对多关系开始:
多对一
所谓多对一,在数据库中即在多的一端添加外键关联到一的一端,比如用户(User)和用户所在组(Group)的关系:一个User只属于一个Group,一个Group有多个Group,并且可以通过User获取所在的Group。通过下面的类图和表关系来看Hibernate的映射实现:
1、首先是实体类User和Group
package tgb.hibernate;
/*
* 用户组
*/
public class Group {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package tgb.hibernate;
/**
* 用户
* @author Jones
*
*/
public class User {
private int id;
private String name;
//关联用户组属性(在用户实体中添加用户组,表明通过用户可以看到所在组)
private Group group;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
}
我们在多的一端(User)添加Group的属性引用,这样可以通过User对象得到Group对象。
2、映射文件配置
用户组Group.hbm.xml
<hibernate-mapping>
<class name="tgb.hibernate.Group" table="t_group">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
用户User.hbm.xml
<hibernate-mapping>
<class name="tgb.hibernate.User" table="t_user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="group" column="groupid" cascade="save-update"/>
</class>
</hibernate-mapping>
通过配置文件我们看到,多对一关系映射的实现是:
在多的一端(User)采用如下标签映射:
<many-to-onename="group" column="groupid"/>
映射成功后,后续就是对对象的操作(增删改查-持久化到数据库),用了以上映射,我们可以通过Hibernate的级联操作对实体进行快捷操作。但是这样还不够,这里就来看级联操作的问题。例如我们需要添加用户组和组下的用户:
级联问题:
session = HibernateUtils.getSession();
session.beginTransaction();
Group group = new Group();
group.setName("系统管理员");
User user1 = new User();
user1.setName("张三");
user1.setGroup(group);
User user2 = new User();
user2.setName("李四");
user2.setGroup(group);
session.save(user1);
session.save(user2);
//在清理缓存是发生错误TransientObjectException
//因为Group为Transient状态,没有被session,在数据库中没有匹配的数据
//而User为Persistent状态,在清理缓存时hibernate在缓存中无法找到Group对象
//结论:Persistent状态的对象不能引用Transient状态的对象
session.getTransaction().commit();
正如注释所说会发生TransientObjectException错误,在保存对象(user)过程中,该对象有一个属性(外键)关联的另外一个未持久化的对象(未保存的对象group),解决方法是先保存"被关联"的那个对象,才能保存先前的那个对象,当然我们这样可以实现我们的需要,但是Hibernate提供了更快捷的方式,下面看Hibernate的级联操作和配置。
级联操作
级联是对象之间的连锁操作,它只影响添加、删除和修改。Hibernate对级联的控制是在配置文件的关系标签中通过cascade进行配置。
还以添加用户组和用户为例,我们需要对<many-to-one>中添加:
<many-to-one name="group" column="groupid" cascade="save-update"/>
“cascade”就来说明在操作两个或多个关联对象,当对其中一个对象进行某种操作时,是否对其关联的对象也作类似的操作。比如我们保存用户是是否也同时保存其所在组。
级联(Cascade)还有如下常用属性:
(1)none:在保存,删除或修改当前对象时,不对其附属对象(关联对象)进行级联操作。它是默认值。
(2)save-update:在保存,更新当前对象时,级联保存,更新附属对象(临时对象,游离对象)。
(3)delete:在删除当前对象时,级联删除附属对象。
(4)all:所有情况下均进行级联操作,即包含save-update和delete等等操作。
(5)delete-orphan:删除此对象的同时删除与当前对象解除关系的孤儿对象(仅仅使用于一对多关联关系中)。
总结
Hibernate中的关系映射反应的就是类之间的关系,关系数据库中就是主外键关系,数据库和UML我们都很熟了,映射也就没有问题,当然关系映射在Hibernate是最基础的内容了,项目实践是必不可少了。下文继续Hibernate的一对多映射(包括双向映射)。