上篇文章讨论了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值。

[java] view plain copy
  1. public class User {
  2. private String name;
  3. public String GetName(){
  4. return name;
  5. }
  6. public void SetName(String name){
  7. this.name=name;
  8. }
  9. private Group group;
  10. public Group GetGroup(){
  11. return group;
  12. }
  13. public void SetGroup(Group group){
  14. this.group=group;
  15. }
  16. }

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

[html] view plain copy
  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <!-- Generated 2014-5-14 23:39:25 by Hibernate Tools 3.4.0.CR1 -->
  5. <hibernate-mapping>
  6. <class name="com.hibernate.User" table="USER">
  7. <id name="id" type="java.lang.Long">
  8. <column name="ID" />
  9. <generator class="assigned" />
  10. </id>
  11. <!-- name的值group为User.java中的一个对应的one中的一个属性,它会自动在表中生成一列,所以使用column对列进行了重命名 -->
  12. <many-to-one name="group" column="groupid"></many-to-one>
  13. </class>
  14. </hibernate-mapping>

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

[java] view plain copy
  1. public void testSave1(){
  2. Session session=null;
  3. try{
  4. session=GetSession.getSession();
  5. session.beginTransaction();
  6. Group group=new Group();
  7. group.SetName("动力节点");
  8. User user1=new User();
  9. user1.SetName("张三");
  10. user1.SetGroup(group);
  11. User user2=new User();
  12. user2.SetName("李四");
  13. user2.SetGroup(group);
  14. session.save(user1);
  15. session.save(user2);
  16. //会报TransientObjectException错误
  17. //在清理缓存时发生错误TransientObjectException
  18. //因为Group为Transient状态,没有被Session,在数据库中没有匹配的数据
  19. //而User为Persistent状态,在清理缓存时Hibernate在缓存中无法找到Group对象
  20. //揭露:Persistent状态的对象不能引用Transient状态的对象
  21. //该问题在testSave2方法中更改
  22. session.getTransaction().commit();
  23. }catch(Exception e){
  24. e.printStackTrace();
  25. session.getTransaction().rollback();
  26. }finally{
  27. GetSession.CloseSession(session);
  28. }
  29. }

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

[java] view plain copy
  1. public void testSave2(){
  2. Session session=null;
  3. try{
  4. session=GetSession.getSession();
  5. session.beginTransaction();
  6. Group group=new Group();
  7. group.SetName("动力节点");
  8. session.save(group);       //此处将group对象设置为Persistent对象
  9. User user1=new User();
  10. user1.SetName("张三");
  11. user1.SetGroup(group);
  12. User user2=new User();
  13. user2.SetName("李四");
  14. user2.SetGroup(group);
  15. session.save(user1);
  16. session.save(user2);
  17. //可以正确的保存数据
  18. //因为Group和User都是Persistent状态的对象
  19. //所以在Hibernate清理缓存时在session中可以找到关联对象
  20. session.getTransaction().commit();
  21. }catch(Exception e){
  22. e.printStackTrace();
  23. session.getTransaction().rollback();
  24. }finally{
  25. GetSession.CloseSession(session);
  26. }
  27. }

2、级联映射

除了上面所说的将group对象和user对象都转化到Persistent对象外,还可以使用cascade级联映射属性,在<many-to-one>属性中添加cascade属性,并复制为save-update,在group对象并非为Persistent状态时即可写入数据库。这样只需要将两个user对象的Group属性设置为同一个group对象即可实现多对一的映射关系,此时User.hbm.xml中对应的内容为如下代码:

[html] view plain copy
  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  3. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  4. <!-- Generated 2014-5-14 23:39:25 by Hibernate Tools 3.4.0.CR1 -->
  5. <hibernate-mapping>
  6. <class name="com.hibernate.User" table="USER">
  7. <id name="id" type="java.lang.Long">
  8. <column name="ID" />
  9. <generator class="assigned" />
  10. </id>
  11. <!-- 级联修改表 -->
  12. <many-to-one name="group" column="groupid" cascade="save-update"></many-to-one>
  13. </class>
  14. </hibernate-mapping>

Note:cascade设置为save-update后即可实现向数据库中级联修改、添加和删除,但是具体的级联查询操作却不可以。
       对应的测试配置文件的方法为如下代码:

[java] view plain copy
  1. //级联cascade
  2. public void testSave3(){
  3. Session session=null;
  4. try{
  5. session=GetSession.getSession();
  6. session.beginTransaction();
  7. Group group=new Group();
  8. group.SetName("动力节点");
  9. User user1=new User();
  10. user1.SetName("张三");
  11. user1.SetGroup(group);
  12. User user2=new User();
  13. user2.SetName("李四");
  14. user2.SetGroup(group);
  15. session.save(user1);
  16. session.save(user2);
  17. //没有抛出TransientObjectException异常
  18. //因为使用了级联
  19. //Hibernate会首先保存User的关联对象Group
  20. //Group和User就都是Persistent状态的对象了
  21. session.getTransaction().commit();
  22. }catch(Exception e){
  23. e.printStackTrace();
  24. session.getTransaction().rollback();
  25. }finally{
  26. GetSession.CloseSession(session);
  27. }
  28. }

3、对比升华

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

结语

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

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

  1. Hibernate 关联映射 之 多对多 关联(二) 之拆分

    1.由问题引出一个多对多拆分成两个多对一 问题:Hibernate 关联映射 之 多对多 关联(一)中中间表只是一个存放用户和角色的表,并无其他作用,如果客户有其他的需求,该表就无法扩展. 2.问题解 ...

  2. 【Hibernate步步为营】--映射合集汇总

    前几篇文章详细讨论了对象模型到关系模型的转化方法,对映射关系做了详细的了解,Hibernate将对象模型转化为相应的关系模型是通过使用相应的映射来完成的(同样也可以使用注解),对于对象之间的关系的转化 ...

  3. Hibernate 关联映射 之 多对多 关联(一)

    1.了解几个知识点: 一.一般的设计中,多对多关联映射,需要一个中间表 二.Hibernate会自动生成中间表 三.Hibernate使用many-to-many标签来表示多对多的关联 四.多对多的关 ...

  4. hibernate关联映射:多对一、一对一

    配置对象关联关系 - 单向一对多关系 - 例如:班级与学生 Grade类中 public class Grade{private int gid;private String gname;privat ...

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

    2018-11-10  22:27:02开始写 下图内容ORM.Hibernate介绍.hibername.cfg.xml结构: 下图内容hibernate映射文件结构介绍 下图内容hibernate ...

  6. 【Hibernate框架】关联映射(多对多关联映射)

    按着我们的总结行进计划,接下来,就是有关于多对多映射的总结了. 我们来举个例子啊,很长时间以来,房价暴涨不落,但是还有很多人拥有很多套房产,假如说,一个富豪拥有九套房产,家里人么准去住哪一套,我们就以 ...

  7. 初识Hibernate之关联映射(一)

    上篇文章我们对持久化对象进行的学习,了解了它的三种不同的状态并通过它完成对数据库的映射操作.但这都是基于单张表的操作,如果两张或者两张以上的表之间存在某种关联,我们又该如何利用持久化对象进行操作呢?本 ...

  8. 8 Hibernate:关联映射(Associations)

    Hibernate 关联映射(Associations)分为: 8.1 Hibernate:一对一关联映射    8.1.1 Hibernate:一对一单向关联(unidirectional)     ...

  9. chapter6 Hibernate的关联映射

    回顾 ·          下面这个异常是怎么回事?该怎么解决? org.hibernate.MappingException: Unknownentity: com.aptech.jb.entity ...

最新文章

  1. 屏蔽firefox浏览器连接失败页面的广告
  2. 【深度学习】卷积神经网络速成
  3. Nginx+Tomcat负载均衡
  4. Python3 爬虫学习笔记 C11【数据储存系列 — MongoDB】
  5. 用 Java 拿下 HTML,分分钟写个小爬虫!
  6. paper reading:[renormalization]Semi-supervised Classification with Graph Convolutional Networks
  7. svn上传文件最大多少_SVN控制上传文件尺寸
  8. 20145240《Java程序设计》第二周学习总结
  9. php如何优化递归函数,php递归函数怎么用才有效?php递归函数典型例子
  10. Oracle 数据库安装教程(11g)
  11. 关于高德地图自定义地图样式只显示中国的解决方案
  12. Mac上好用的视频播放器有哪些?
  13. 2020年岁末的年终总结——来自一位70后的大龄程序员的总结分享
  14. conda: No writeable envs directories configured.
  15. 无法安装驱动程序此计算机上不存在,在win7中安装打印机时,如果“找不到打印机驱动程序包所需的核心驱动程序包”怎么办?...
  16. Vue 视频音频播放
  17. shopex4.8.5 php5.6,ShopEx(网上商店系统)
  18. IPV6在容器云中的部署(一)
  19. python判断字符串是否为大写字母、小写字母
  20. 常见的三种字符编码ASCII、Unicode、UTF-8

热门文章

  1. 【Linux 内核 内存管理】RCU 机制 ④ ( RCU 模式下更新链表项 list_replace_rcu 函数 | 链表操作时使用 smp_wmb() 函数保证代码执行顺序 )
  2. 【Linux 内核】实时调度类 ⑦ ( 实时调度类核心函数源码分析 | dequeue_task_rt 函数 | 从执行队列中移除进程 )
  3. 【错误记录】Groovy 闭包使用报错 ( 闭包中不能直接使用外部对象的方法 | 需要先设置 delegate 代理 )
  4. 【BLE MIDI】推荐一个 Android 平台开源 MIDI 软件 MidiSheetMusic ( 相关资料 | Android Studio 中导入 Eclipse 源码 )
  5. 【Android APT】编译时技术 ( 编译时注解 和 注解处理器 依赖库 )
  6. 【Android 高性能音频】Oboe 开发流程 ( 检查 Oboe 音频流属性 | 开始播放 | 停止播放 | 关闭 Oboe 音频流 | 重新配置 Oboe 音频流属性 )
  7. 【Android FFMPEG 开发】FFMPEG 初始化 ( 网络初始化 | 打开音视频 | 查找音视频流 )
  8. Linux Shell的输入彩色字体
  9. 2-02字符编码的演化
  10. Float浮点数的使用和条件