双向多对一关联映射在开发中用的非常多,为了更好的理解双向多对一,我们先来讨论单向多对一的关联映射,下面我们通过一个例子来体会,用户和组的关系为多对一,多个用户属于一个组,类图如下:
注:上图表示单向的关联,即通过User可以关联到Group,而通过Group不能关联到User。
映射的数据库表为:
注:从映射的数据库表可以看出,多个用户对应一个组,即多对一关系。
User.java:
public class User {
/*用户ID*/
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;
}
}
Group.java:
public class Group {
/*组ID*/
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;
}
}
User.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lixue.bean">
<class name="User" table="t_user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<!-- 设置用户和组的关系(单向多对一,在多的一端加一个外键,通过外键来维护关系)cascade="save-update"表示级联保存和级联更新 -->
<many-to-one name="group" column="group_id" cascade="save-update"/>
</class>
</hibernate-mapping>
注:多对一关联映射是在多的一端加一个外键来维护关系。cascade表示级联,如上述代码中配置的cascade="save-update"表示级联保存和级联更新,如果没有设置级联,那么必须显示的保存对象,否则会报错。
Group.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lixue.bean">
<class name="Group" table="t_group">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
注:因为是单向的,所以Group这个配置文件就是普通的属性映射。
测试类:
public void testSave1(){
/*定义Session*/
Session session = null;
/*定义Transaction*/
Transaction transaction = null;
try {
session = HibernateUtils.getSession();
transaction = 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状态的对象
*/
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally{
HibernateUtils.closeSession(session);
}
}
注:上述代码是在User.hbm.xml文件没有配置cascade="save-update"的前提下测试的,上述程序没有显示的保存group,并且配置文件没有设置级联保存,所以会发生错误:TransientObjectException即处于Persistent状态的User对象不能引用处于Transient状态的Group对象。
测试二:
public void testSave2(){
/*定义Session*/
Session session = null;
/*定义Transaction*/
Transaction transaction = null;
try {
session = HibernateUtils.getSession();
transaction = 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(group);
session.save(user1);
session.save(user2);
/**
* 提交事物
* 这种方式可以正确的保存数据,因为Group和User都是Persistent状态的数据
* 在Hibernate清理缓存时在Session中可以找到关联对象
*/
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally{
HibernateUtils.closeSession(session);
}
}
注:上述代码可以成功保存对象,虽然配置文件中没有设置级联保存,但是程序中显示的调用了保存group的代码:session.save(group);所以group对象就成了Persistent状态了,必然可以保存成功。
测试三:
/**
* 通过上面这种方式即同时保存组合User的方式提交事务是没有问题的,
* 其实还有一种更简单的方式,那就是在XML文件中配置级联操作
* cascade="save-update"
*/
public void testSave3(){
/*定义Session*/
Session session = null;
/*定义Transaction*/
Transaction transaction = null;
try {
session = HibernateUtils.getSession();
transaction = 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(group);
session.save(user1);
session.save(user2);
/**
* 提交事物
* 这种方式可以正确的保存数据,因为设置了级联保存和级联更新
* 在Hibernate清理缓存时在Session中可以找到关联对象
*/
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally{
HibernateUtils.closeSession(session);
}
}
注:上述程序是在User.hbm.xml文件中配置了cascade="save-update"的前提下测试的,因为配置了级联保存和级联更新,所以即使在上述测试中没有显示的保存group对象,依然可以成功的保存。
测试四:
/**
* 单向多对一关联
* 我们可以通过多的一端加载一的一端
* 即通过User这一端加载Group这一端
*/
public void testFind(){
/*加载User*/
User user = (User) HibernateUtils.getSession().get(User.class, 1);
/*打印用户名*/
System.out.println("user.name=" + user.getName());
/*打印用户所在组*/
System.out.println("user.group.name=" + user.getGroup().getName());
}
注:我们的应用时单向多对一关联映射,即通过多的一端即User,可以加载一的一端即Group的一端。