双向多对一关联映射在开发中用的非常多,为了更好的理解双向多对一,我们先来讨论单向多对一的关联映射,下面我们通过一个例子来体会,用户和组的关系为多对一,多个用户属于一个组,类图如下:
注:上图表示单向的关联,即通过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的一端。