【Hibernate步步为营】--(一对多映射)之双向关联
来源:互联网 发布:linux so文件反编译 编辑:程序博客网 时间:2024/05/08 08:08
一、一对多双向关联
这里继续采用上篇文章的学生和班级作为示例,班级和学生之间是一对多的关系,一个班级中拥有多名学生,和上篇文章不同的是这里的关系是双向的,也就是一的一端和多的一端同时维护关联关系,所以它的对象图如下:
对应的关系模型图没有太大的变化,因为它们之间的关系是双向的,所以在关系模型中两端同时维护关联关系,映射到关系模型中如下图所示:
在一对多的单向关联中映射文件只需要在一的一端进行特殊配置就可以,使用<one-to-many>配置,并在对象模型中使用set迭代器来设置外联的对象模型,但是不同的是在双向的关联中需要在多的一端添加对应的另一端的外键关联,这时候就必须在多的一端使用<many-to-one>的关联关系来标明这种双向性。
1、映射
这里还使用Classes和Student来做示例,在Classes一端的内容和上文相同不会发生变换,但是多的一端Student的配置会发生变化,也就是在映射文件中需要添加<many-to-one>标签。
Student.hbm.xml映射文件配置需要添加外键列<many-to-one>标签,并且该列的名称要和Classes.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>
- <class name="com.src.hibernate.Student" table="t_student">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <!-- 在多的一端Student中添加一行新的Classes列 ,并且列的名称要和Classes.hbm.xml的列明相同-->
- <many-to-one name="classes" column="classesid"></many-to-one>
- </class>
- </hibernate-mapping>
Classes.hbm.xml映射文件的配置和上篇文章相同,需要注意的是在Classes.java文件中添加了set属性映射对应了Student对象,所以在映射文件中需要添加set标签来指示为对象模型中使用了set迭代器,具体配置如下代码:
- <?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.src.hibernate.Classes" table="t_classes">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <set name="students" inverse="true">
- <key column="classesid"></key>
- <one-to-many class="com.src.hibernate.Student"></one-to-many>
- </set>
- </class>
- </hibernate-mapping>
2、类
映射文件的配置是直接对应着类来的,所以有了映射文件就能够写出相应的类,相同的有了类就能够知道对应的映射文件如何编写,那来看看相应的类代码如何编写。
Student.java类,需要在类中添加关联的班级对象属性,在加载Student时能获得Classes的相关信息。
- package com.src.hibernate;
- public class Student {
- //关联的班级对象
- private Classes classes;
- public Classes getClasses() {
- return classes;
- }
- public void setClasses(Classes classes) {
- this.classes = classes;
- }
- //学生id
- private int id;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- //学生姓名
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
Classes.java文件具体代码内容见上篇文章,这里就不在详述。
有了对象模型接下来生成关系模型,生成的SQL语句如下:
- alter table t_student drop foreign key FK4B907570FC588BF4
- drop table if exists t_classes
- drop table if exists t_student
- create table t_classes (id integer not null auto_increment, name varchar(255), primary key (id))
- create table t_student (id integer not null auto_increment, name varchar(255), classesid integer, primary key (id))
- alter table t_student add index FK4B907570FC588BF4 (classesid), add constraint FK4B907570FC588BF4 foreign key (classesid) references t_classes (id)
3、数据操作
建立表结构后来编写测试方法来验证数据的操作,首先来看看数据的插入,向表结构中插入数据,写入数据时会有两种情况,一种是首先创建一个Classes对象,并将对象写入到数据库中,然后创建Student对象,在Classes对象中添加学生对象;另外一种是先创建学生对象,并将学生对象写入数据库中,然后创建Classes对象将学生对象加入到Classes对象中,这两种类型的操作最后是不相同的,来对比下。
3.1 先写班级后写学生
先把班级写入到数据库中后,Classes对象进入了Transient状态,并在数据库中有了一行,这时再写Student对象,Student对象会查找对应的Classes的主键将其写入到表中,所以此时关系模型中的数据都是非空的,保存的代码如下:
- public void testSave(){
- Session session=null;
- try{
- //创建session对象
- session=HibernateUtils.getSession();
- //开启事务
- session.beginTransaction();
- //创建班级对象,将班级对象写入到数据库中
- Classes classes=new Classes();
- classes.setName("class");
- session.save(classes);
- //创建学生1对象,将学生对象写入到数据库中
- Student student1=new Student();
- student1.setName("zhangsan");
- student1.setClasses(classes);
- session.save(student1);
- //创建学生2对象,将学生对象写入到数据库中
- Student student2=new Student();
- student2.setName("lisi");
- student2.setClasses(classes);
- session.save(student2);
- session.getTransaction().commit();
- }catch(Exception e){
- e.printStackTrace();
- session.getTransaction().rollback();
- }finally{
- HibernateUtils.closeSession(session);
- }
- }
对应的写入数据库中的信息列表如下图:
3.2 先写学生后写班级
先把学生写入到数据库中此时因为学生表需要获取对应的班级列的主键信息,但是因为班级信息转化到Transient状态,所以在写入学生信息时会有null值,代码如下:
- public void testSave(){
- Session session=null;
- try{
- //创建session对象
- session=HibernateUtils.getSession();
- //开启事务
- session.beginTransaction();
- //创建学生1对象,将学生对象写入到数据库中
- Student student1=new Student();
- student1.setName("zhangsan");
- session.save(student1);
- //创建学生2对象,将学生对象写入到数据库中
- Student student2=new Student();
- student2.setName("lisi");
- session.save(student2);
- //创建班级对象
- Classes classes=new Classes();
- classes.setName("Classes");
- //设置学生集合
- Set students=new HashSet();
- students.add(student1);
- students.add(student2);
- //将学生集合写入到Classes中
- classes.setStudents(students);
- //可以成功保存数据
- //但是会发出多余的update语句来维持关系,因为是一对多的原因
- session.save(classes);
- session.getTransaction().commit();
- }catch(Exception e){
- e.printStackTrace();
- session.getTransaction().rollback();
- }finally{
- HibernateUtils.closeSession(session);
- }
- }
写入后对应的数据库视图如下:
对比两种写入操作,因为两个写入的先后顺序不同所以出现了不同的结果,但因为是双向的关联关系所以在写入时并不会发生异常。
4、读取操作
- public void testLoad1(){
- Session session=null;
- try{
- session=HibernateUtils.getSession();
- session.beginTransaction();
- //通过班级读取学生信息
- Classes classes=(Classes)session.load(Classes.class,1);
- System.out.println("classes.name="+classes.getName());
- Set students=classes.getStudents();
- for(Iterator iter=students.iterator();iter.hasNext();){
- Student student=(Student)iter.next();
- System.out.println("student.name="+student.getName());
- }
- //通过学生信息读取班级信息
- Student stu=new Student();
- stu=(Student)session.load(Student.class, 1);
- System.out.println("通过学生加载班级信息Classes.id= "+stu.getClasses().getId());
- session.getTransaction().commit();
- }catch(Exception e){
- e.printStackTrace();
- session.getTransaction().rollback();
- }finally{
- HibernateUtils.closeSession(session);
- }
- }
运行上面的测试语句,生成的对应的语句信息如下:
- Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?
- classes.name=class
- Hibernate: select students0_.classesid as classesid1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_, students0_.classesid as classesid0_0_ from t_student students0_ where students0_.classesid=?
- student.name=lisi
- student.name=zhangsan
- 通过学生加载班级信息Classes.id= 1
结语
- 【Hibernate步步为营】--(一对多映射)之双向关联
- 【Hibernate步步为营】--(一对多映射)之双向关联
- 【Hibernate步步为营】--(一对多映射)之双向关联
- 【Hibernate步步为营】--(一对多映射)之单向关联
- 【Hibernate步步为营】--(一对多映射)之单向关联
- 【Hibernate步步为营】--(一对多映射)之单向关联
- 【Hibernate步步为营】--(一对多映射)之单向关联
- 步步为营Hibernate全攻略(二)剪不断理还乱之:一对多关联映射
- Hibernate 关联映射之---- 一对多双向映射
- hibernate一对多关联映射(双向关联)(转)
- Hibernate 一对多关联映射(双向关联)
- Hibernate关联映射 --- 一对多实例分析(双向关联)
- Hibernate一对多关联双向映射
- hibernate映射一对多双向关联关系
- Hibernate一对多双向关联映射
- Hibernate一对多双向关联映射
- hibernate 关联映射 双向多对一(一对多)
- hibernate笔记(双向一对多关联映射)
- JAVAtwo 总结
- 开发人员应该知道的安全知识
- Python容器类型
- ORACLE表锁定的解决办法
- 新手做LeetCode N16 3Sum Closest
- 【Hibernate步步为营】--(一对多映射)之双向关联
- DB2命令行操作指令
- 18. 4Sum
- 微服务实战(三):深入微服务架构的进程间通信
- wxPython在Windows下的安装
- NYOJ.891_找点问题
- 150. Evaluate Reverse Polish Notation
- C#/IOS/Android通用加密解密方法
- 【Hibernate步步为营】--多对多映射详解