【Hibernate步步为营】--关联映射之多对一

上篇文章讨论了Hibernate的基本映射,一个实体类对应着一张表,在相应的Hibernate Mapping文件中使用<class>标签映射。并且实体类中的普通属性对应着表字段,使用<property>标签映射。另外在构造实体类时应注意:在实体类中应实现无参的默认的构造函数,提供一个标示,建议不要使用final修饰实体类,为实体类生成getter和setter方法,最后介绍了几种主要的主键生成策略,接下来讨论多对一映射。

一、关联映射之多对一

对于多对一关联映射其实很容易理解,在思考时可以把它看做人员和组之间的关系,在一个组中会有多个人员,所以这就出现了多对一的关系多个人会同属于一个组,那么在设计关系模型时就会有两种设计方法,一种是将组号作为外键添加到用户表中,另外一种是单独生成第三张表,将用户id号和组id号相关联。对于第一种设计方法它的关系模型可用下表表示:

这种多对一关联映射反应到对象模型中它是一种聚合关系,User是group的一部分,group中存在User,它们两个的生命周期是不相同的,可反应为下图:

那么这种多对一关系映射在Hibernate中是如何设置的呢?下面将会介绍两种方法,使用<many-to-one>标签直接映射,或者使用<many-to-one>的cascade级联修改表。

1、Many-to-one直接映射

从字面意思上就能够理解,它是指多对一的关系,many指的是多的一端,one指的是少的一端,在使用时往往在多的一端的hbm中使用该标签,并将<many-to-one>的name属性设置为该映射文件对应的类中的one一端的属性,如:<many-to-one name="group" column="groupid"></many-to-one>,该标签添加在了User.hbm.xml中,它对应many;标签中的name值为group对映射one,并且在User.java中会有一个名为group的属性。接下来看下具体实现实现的代码类。

(1)User.java类代码,其中有一个名为group的属性,它将会作为<many-to-one>的one一端的name值。

public class User {
	private String name;
	public String GetName(){
		return name;
	}
	public void SetName(String name){
		this.name=name;
	}

	private Group group;
	public Group GetGroup(){
		return group;
	}
	public void SetGroup(Group group){
		this.group=group;
	}
}

(2)User.hbm.xml中的<many-to-one>,name的值为User.java中one端的属性值,它会在数据库中生成一个新列,可以将该新列理解为User表的外键。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2014-5-14 23:39:25 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.hibernate.User" table="USER">
        <id name="id" type="java.lang.Long">
            <column name="ID" />
            <generator class="assigned" />
        </id>

        <!-- name的值group为User.java中的一个对应的one中的一个属性,它会自动在表中生成一列,所以使用column对列进行了重命名 -->
        <many-to-one name="group" column="groupid"></many-to-one>

    </class>
</hibernate-mapping>

(3)测试上面的映射关系,向表中写入两个User对象分别为user1和user2,命名为张三和李四,使用session保存对象,向数据库中写入数据,代码如下:

public void testSave1(){

		Session session=null;
		try{
			session=GetSession.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错误
			//在清理缓存时发生错误TransientObjectException
			//因为Group为Transient状态,没有被Session,在数据库中没有匹配的数据
			//而User为Persistent状态,在清理缓存时Hibernate在缓存中无法找到Group对象
			//揭露:Persistent状态的对象不能引用Transient状态的对象
			//该问题在testSave2方法中更改
			session.getTransaction().commit();
		}catch(Exception e){

			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			GetSession.CloseSession(session);
		}
	}

但是使用上面的代码在执行写入时会报错TransientObjectException,这是因为在保存User对象时它会按照<many-to-one>中添加的group去内存中查找group对象,但是上面的代码中group对象一直都是在Transient状态中,并没有被session管理,也就是说查找不到session对象,而User对象进入了Persistent状态,于是会报此错误。正确的代码如下:

public void testSave2(){

	Session session=null;
	try{
		session=GetSession.getSession();
		session.beginTransaction();

		Group group=new Group();
		group.SetName("动力节点");
		session.save(group);       //此处将group对象设置为Persistent对象

		User user1=new User();
		user1.SetName("张三");
		user1.SetGroup(group);

		User user2=new User();
		user2.SetName("李四");
		user2.SetGroup(group);

		session.save(user1);
		session.save(user2);

		//可以正确的保存数据
		//因为Group和User都是Persistent状态的对象
		//所以在Hibernate清理缓存时在session中可以找到关联对象
		session.getTransaction().commit();
	}catch(Exception e){

		e.printStackTrace();
		session.getTransaction().rollback();
	}finally{
		GetSession.CloseSession(session);
	}
}

2、级联映射

除了上面所说的将group对象和user对象都转化到Persistent对象外,还可以使用cascade级联映射属性,在<many-to-one>属性中添加cascade属性,并复制为save-update,在group对象并非为Persistent状态时即可写入数据库。这样只需要将两个user对象的Group属性设置为同一个group对象即可实现多对一的映射关系,此时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">
<!-- Generated 2014-5-14 23:39:25 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.hibernate.User" table="USER">
        <id name="id" type="java.lang.Long">
            <column name="ID" />
            <generator class="assigned" />
        </id>

        <!-- 级联修改表 -->
        <many-to-one name="group" column="groupid" cascade="save-update"></many-to-one>

    </class>
</hibernate-mapping>

Note:cascade设置为save-update后即可实现向数据库中级联修改、添加和删除,但是具体的级联查询操作却不可以。

对应的测试配置文件的方法为如下代码:

//级联cascade
public void testSave3(){

	Session session=null;
	try{
		session=GetSession.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异常
		//因为使用了级联
		//Hibernate会首先保存User的关联对象Group
		//Group和User就都是Persistent状态的对象了
		session.getTransaction().commit();
	}catch(Exception e){
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally{
		GetSession.CloseSession(session);
	}
}

3、对比升华

两种方法同样实现了多对一的映射方法,结果上是相同的,但在实现上很不相同。无论是第一种还是第二种采用的都是<many-to-one>在many一端的映射文件中添加该标签,并将标签的name属性赋值为该映射文件注册的类中的one一端的属性值,这样就完成了多对一的基本映射,这是相同点。不同点是直接映射关系没有采用Hibernate字段的属性,这样在实现上较灵活,不但支持增删改,而且可以查询;第二种的cascade级联修改则采用了Hibernate提供的方法,此种方法只支持增删改,并不支持查询。

结语

文章介绍了两种方法来实现多对一的映射,这两种方法在实现结果上是相同的,都是采用的<many-to-one>多对一标签,实现上很简单。需要注意的是第一种方法必须将组对象和用户全部转化为Transient状态,都必须被Session管理这样在保存时才能够在Session中查找到两种对象。多对一映射是经常使用的,另外还有其它的映射关系,将会在下篇文章中讨论。

【Hibernate步步为营】--关联映射之多对一

时间: 2024-10-12 01:42:02

【Hibernate步步为营】--关联映射之多对一的相关文章

千山万水之Hibernate(七)——关联映射(多对多)

一直认为通过写SQL语句来处理多对多的情况比较复杂,对表关系必须是理解的非常清楚,在学习了Hibernate中的多对多处理后,想想其实多对多也没什么,只不过多了一张表,如果说多了一张表感觉复杂了,Hibernate中我们完全不用去理会他,直接去操作关联实体就可以了,从这点上看,Hibernate为我们做了很多事,确实功不可没. 在有了Hibernate这个帮手后,今天一起看看我们在Hibernate的基础上进行一些操作. 原理分析 我们拿学生和课程为例子来分析,所谓的多对多关系可以这样理解:一个

Hibernate注解----关联映射注解以及课程总结详解----图片版本

上一篇,记录了Hibernate注解----类级别注解以及属性注解详解 ,我们这一节主要讲解的是Hibernate注解----关联映射注解以及课程总结详解. 本节的主要内容: 第3章 关联映射注解 3-1 本章简介 3-2 实体之间的关系 3-3 一对一单向外键关联(一) 3-4 一对一单向外键关联(二) 3-5 一对一双向外键关联 3-6 一对一单向外键联合主键 3-7 多对一单向外键关联(一) 3-8 多对一单向外键关联(二) 3-9 一对多单向外键关联 3-9 一对多双向外键关联 3-10

hibernate 之 关联映射的基于外键的双向一对一关联

1. 人和身份证是一个一对一的关系,他们的表结构为: 2. 类结构:Person.java public class Person { private int id; private String name; private int age; private IdCard idCard; public Person() { } public Person(String name, int age) { super(); this.name = name; this.age = age; } pu

hibernate 之 关联映射的基于外键的单向一对一映射

1. 人和身份证是一个一对一的关系,他们的表结构为: 2. 类结构:IdCard.java : public class IdCard { private String id; private String address; public IdCard() { } public IdCard(String id, String address) { super(); this.id = id; this.address = address; } public String getId() { r

千山万水之Hibernate(四)——关联映射(多对一)

在上一篇文章(<千山万水之Hibernate(三)--基本映射 >)中,我们介绍了怎样通过Hibernate进行最基本的单实体映射以及Hibernate设计的一些基本原理,本篇文章将介绍关联映射中的多对一映射是如何实现的. 原理分析 我们拿学生和班级为示例,学生属于某一个班级,而且多个学生是有可能属于同一个班级的,相应的实体关系图为: Class的映射文件向上一篇我们介绍到的基本映射文件那样编写便可以,而对于Student实体,我们需要从Student中得出对应班级,而Student与Clas

Hibernate,关系映射的多对一单向关联、多对一双向关联、一对一主键关联、一对一外键关联、多对多关系关联

2018-11-10  22:27:02开始写 下图内容ORM.Hibernate介绍.hibername.cfg.xml结构: 下图内容hibernate映射文件结构介绍 下图内容hibernate映射文件中主键自增规则.Hibernate实例状态(瞬时状态.持久化状态.托管状态).Hibernate初始化类获取session等方法 下图内容保存数据过程 下面内容保存数据顺序.查询数据方法 get().load()和延迟加载.删除数据 下图内容删除对象顺序.修改数据顺序 下面内容关联关系映射.

006——hibernate一对一关联映射

一对一关联映射有两种方式:主键关联和外键关联 以夫妻为例(Husband----Wife) 单向关联实体类: Husband.java package com.java.hibernate; public class Husband { private int id; private String name; private Wife wife; public Wife getWife() { return wife; } public void setWife(Wife wife) { thi

9、Hibernate之关联映射(hibernate映射)

回顾Hibernate: 1. hibernate开发环境搭建 --> 引入jar: hibernate.jar + required + jpa + 驱动包 -> hibernate.cfg.xml -> javabean/*.hbm.xml -> Application 测试 2.hibernate api --> Configuration --> SessionFactory --> Session -> Transaction --->Que

hibernate 之 关联映射的双向一对多关联

1. 考虑学生表 和 教师表,表结构和单向的表结构一样. 2. 类结构:Teacher.java public class Teacher { private int id; private String name; private Set<Student> students = new HashSet<Student>(); public Teacher() { } public Teacher(String name) { super(); this.name = name;