千山万水之Hibernate(六)——关联映射(一对多)
2015-05-31 14:03
316 查看
学习Hibernate的过程中,映射是比较重要的一部分,再说其中哪一种映射比较重要(或可以说是比较常用)呢?那一定很多人会想到一对多关联映射。之所以这样说,是因为在生活中很多这样的实例:班级-学生、企业-员工、文件夹-文件、试卷-题目等。至于生活中为什么会遇到这样大量一对多的情况,似乎是哲学方面的事情了,当然大家可以继续思考,而我们今天主要讨论Hibernate中的一对多。
单向关联
由图可知,由于单向的引用,需要Classes来维护班级、学生二者之间的关系,也就是说,当对Student进行增、删、改时,需要Classes来发起之间关系的维护。另一方面,体现到数据库层面上,我们可以想象,怎样可以实现两个表之间的关系呢?这时候我们可以想到之前练习过的多对一的情况。实际上在实现原理方面,这里所说的一对多和多对一是一样的,都需要在多的一端加入一的一端的外键。
(说明:程序中调用HibernateUtils的代码实现可参考:千山万水之Hibernate(四)——关联映射(多对一))
执行打印SQL语句如下:
可以看到,在执行了保存学生、保存班级的SQL语句后,在提交事务时,又发出了更新外键关系的SQL语句,也就是说,在调用session.save(student)的时候,student端不知道classes一端的存在,不能将关联字段添加上。这样出现的问题有两个:
在提交事务、清理缓存时,会发出额外的update语句,更新关系;
如果上面的问题还不至于太严重的后果,那在火上浇油一把:
外键列设置为非空,那将会在执行的过程中报错:
问题解决:
设置一对多为双向关联,将维护关系端设置为多端,一端不再维护关系,这样在添加学生的时候就将关系直接保存,而对于一端的加载,不能构成影响。
设置一端配置控制方向上的反转为true,这样只影响数据的存储,不会影响加载。
原理分析
我们仍然拿班级、学生之间的关系做例子:一个班级包含多个学生。相应的实体关系图为:单向关联
由图可知,由于单向的引用,需要Classes来维护班级、学生二者之间的关系,也就是说,当对Student进行增、删、改时,需要Classes来发起之间关系的维护。另一方面,体现到数据库层面上,我们可以想象,怎样可以实现两个表之间的关系呢?这时候我们可以想到之前练习过的多对一的情况。实际上在实现原理方面,这里所说的一对多和多对一是一样的,都需要在多的一端加入一的一端的外键。
<?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> <class name="com.tgb.hibernate.Classes" table="t_classes"> <id name="id"> <generator class="native"></generator> </id> <property name="name" /> <!--这里采用set标签代表多端,在多端表添加外键列,由一的一端维护此外键关系--> <set name="students"> <key column="classesId" /> <one-to-many class="com.tgb.hibernate.Student" /> </set> </class> </hibernate-mapping>
操作示例
测试程序如下:public void testSave2(){ Session session = null; Transaction tx = null; try{ session = HibernateUtils.getSession(); tx = session.beginTransaction(); Student student1 = new Student(); student1.setName("李雷"); session.save(student1); Student student2 = new Student(); student2.setName("韩梅梅"); session.save(student2); Set<Student> students = new HashSet<Student>(); students.add(student1); students.add(student2); Classes classes = new Classes(); classes.setName("TGB——9"); classes.setStudents(students); session.save(classes); tx.commit(); }catch(Exception e){ e.printStackTrace(); if(tx != null){ tx.rollback(); } }finally{ HibernateUtils.closeSession(session); } }
(说明:程序中调用HibernateUtils的代码实现可参考:千山万水之Hibernate(四)——关联映射(多对一))
执行打印SQL语句如下:
Hibernate: insert into t_students (name) values (?) Hibernate: insert into t_students (name) values (?) Hibernate: insert into t_classes (name) values (?) Hibernate: update t_students set classesId=? where id=? Hibernate: update t_students set classesId=? where id=?
可以看到,在执行了保存学生、保存班级的SQL语句后,在提交事务时,又发出了更新外键关系的SQL语句,也就是说,在调用session.save(student)的时候,student端不知道classes一端的存在,不能将关联字段添加上。这样出现的问题有两个:
在提交事务、清理缓存时,会发出额外的update语句,更新关系;
如果上面的问题还不至于太严重的后果,那在火上浇油一把:
外键列设置为非空,那将会在执行的过程中报错:
ERROR JDBCExceptionReporter:72 - Field 'classesId' doesn't have a default value
问题解决:
设置一对多为双向关联,将维护关系端设置为多端,一端不再维护关系,这样在添加学生的时候就将关系直接保存,而对于一端的加载,不能构成影响。
设置一端配置控制方向上的反转为true,这样只影响数据的存储,不会影响加载。
总结
我们会说,设置关联上的映射关系,通常会根据业务需求来设计,但这里的双向关联,主要是解决一对多单向关联的缺陷,而不是需求驱动的。因此我们的设计也应该灵活,不能严守教条,既要结合具体的业务,也要考虑技术的实现程度。相关文章推荐
- 若不主动,就没有然后了。
- ttl电平和rs232电平
- 【Robotium】学习笔记(二)
- 多线程(二)生命周期和同步
- R语言在Ubuntu下的安装及使用
- 第12周项目2-摩托车继承自行车和机动车
- Java安全之数字签名
- 多线程(二)生命周期和同步
- oracle 简答创建一个监听器记录对某张表的操作记录
- VNC相关使用
- Atlassian 敏捷组合的相关问题
- Searching: Sequential Search In Ordered Table
- 根据IP定位用户所在城市信息 (js)
- 静态变量
- 静态变量
- 黑马程序员——Java错误笔记——static
- mysql基础
- 2015年百度之星程序设计大赛 - 初赛(1)
- android 开始提升视觉效果的10个建议
- 选择排序优化算法