hibernate一对多关联映射——双向(非常常用)

原文地址:http://blog.sina.com.cn/s/blog_5fad23090100fct8.html
一对多关联映射通常设为双向的,关系字段设hibernate一对多关联映射鈥斺斔颍ǚ浅3S茫置在一的一端,而且在多的一端维护,
一、解析:
1、  一对多双向关联也就是说,在加载班级时,能够知道这个班级所有的学生。
同时,在加载学生时,也能够知道这个学生所在的班级。
2、我们知道,一对多关联映射和多对一关联映射是一样的,都是在多的一端加外键了。
只不过是角度不同,我们从Classes一端来看,它就是一对多,它维护了一个一指向多的关系。在加载班级时,能够把学生加载上来。返过来,多个学生是属于一个班级的,它就是多对一。
3、而像我们的用户和组,就是多对一。多对一维护了一种知道,就是多指向一,所以在加载学生时,就能拿到这个学生所在的班级。如果能够拿到它所在的班级,那么必须在对象模型Student中,持有Classes对象的一个引用。
 hibernate一对多关联映射鈥斺斔颍ǚ浅3S茫
所以要在Student实体模型中,加入一个字段classes。这样才能实现双向的一对多的关联映射。
 
二、新建hibernate_one2many_2项目
三、修改对象模型 :
1、在Student.class中加上classes字段。
package com.bjsxt.hibernate;
 
public class Student {
    private int id;
    private String name;
    Classes classes;
    public Classes getClasses() {
       return classes;
    }
    public void setClasses(Classes classes) {
       this.classes = classes;
    }
    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;
    }
 
}
2、Classes类的内容不变
package com.bjsxt.hibernate;
 
import java.util.Set;
 
public class Classes {
    private int id;
    private String name;
    private Set students;
    public Set getStudents() {
       return students;
    }
    public void setStudents(Set students) {
       this.students = students;
    }
   
    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;
    }
 
}
 
这样就实现双向了,Student类中有Class对象的引用。
同样,Classes类中有关Student对象的引用。
四、修映射文件。
1、修改改Student.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.bjsxt.hibernate.Student"  table="t_student">
       <id name="id">
           <!-- 主键的生成方式不能是uuid。因为这种生成方式是生成32位16进制的字符串
              而我们的实体类中id的类型为int.所以要修改主键的生成方式为native.就是以数字形式自增 -->
           <generator class="native"></generator>
       </id>
       <property name="name"/>
      
       <!-- 因为在Student端加了一个属性,所以在Student的映射文件里面要加上这个属性的映射
         它的映射就是<many-to-one>
         name就是Student类中的属性名classes
         如果只指定属性名,而没有指定列,就会在我们的表t_student中加入一个字段,名字是classes.
         并且这个字段也指向一的一端。
         可是在存储时,classes这段是不会存的。
         原因是t_classes表根本不知道这个字段的存在。
         因为在Classes.hbm.xml映射文件中,根本就没有看到classes这个字段的影子。
         如果像我们的用户和组一样,在我们的多的一端存,那么这个字段就可以存了,因为在
         多的一端,也就是Student.hbm.xml文件中,配置了它。
         就如用户和组,我们new一个用户,然后new一个组,然后把这个用户设到这个组里,这样就存起来了。
         但是,如果在多的一端存,则classesid字段就存不上了。因为在Student.hbm.xml文件中,
         根本就没有classesid这个字段的关系。所以也就不知道 classesid的存在。
         
         所以,在映射时,映射的字段必须跟一对多那边的名字一模一样。我们在<many-to-oen>标签,用column属性来指定
         
         所以说,在一对多双向关联映射中,在多的一端只有一个外键约束,就是跟在一的一端映射的列名相同的列名。
         也就是说,从多的一端存,与在一的一端存,都会存到这个字段classesid上。
         
         我们说,many-to-one映射会加字段,可是在一对多双向关联映射中,这个关联的字段已经有了,所以就不用再加了。
         -->
       <many-to-one name="classes" column="classesid"/>
         
    </class>
</hibernate-mapping>
2、  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 package="com.bjsxt.hibernate">
    <class name="Classes"  table="t_classes">
       <id name="id">
         
           <generator class="native"></generator>
       </id>
       <property name="name"/>
       <!-- 上面为简单属性
         下面要看一下集合要如何映射
         答:集合要用set标签来映射
        set标签的名字属性的值就是Classes类中的集合类型数据的变量名 -->
       <set name="students">
          <!-- 那么要如何映射set里面的东西呢?
             我们知道ont2many的映射关系,与many2one的映射是一样的,要在多的一端加字段的。
             但是到目前为止,在t_student这张表里面是没有classid这个字段标识的。
             所以要把这个字段加过来,加过来之后,还要做为外键指向t_classes这张表的主键
             我们用key标签来实现 -->
             <!-- 在key标签里面要使用的属性是列,就是给定在t_student表中的加上的列的名字。
             加了key这个标签后,就会把classid这个字段加入到t_student这张表里面了,
             它做为外键指向t_class表的id字段。 -->
          <key column="classesid"></key>
          <!-- 接下来,采用one-to-many这个标签,采用这个标签,一方面是一对多,
             另一方面,要用class这个属性来指定Classes.java类中的students这个集合里面
             到底是什么元素。我们这个实例里面是Student对象集合。
             一定要指定集合中的元素是什么类型的对象。-->
          <one-to-many class="Student"/>
          <!--ont-to-many标签就代表了students集合里面是Student对象。
          这样指明之后,classes就能找到student这张表。
          因为student在这里都写了。
          而且在Student.hbm.xml文件中,也已经映射Student对象映射到哪张表了。
          这样就可以把classid加到表t_student里面,而且做为外键指向t_classes表的主键id. -->
       </set>
    </class>
</hibernate-mapping>
五、数据库就不用创建了,用一对多单向关联那个数据库。
六、表也不用导出了,就用一对多单向关联那个数据库的表了。
七、测试存储
1、在多的一端存储
解析:在Student中,已经持有了Classes,并且它们之间的关系也已经配置了。
所以我们现在可以new出classes来,然后调用 student的setClasses()方法,将classes设进去就可以了。
public void testSave3(){
       Session session=null;
       try{
           session=HibernateUtils.getSession();
           session.beginTransaction();
          
           Classes classes=new Classes();
           classes.setName("理想Database实验室");
           //瞬时对象要保存。不然在后面的代码中,保存Student时,会因为classs在数据库中没有而发生瞬时对象不存在异常
           session.save(classes);
          
           Student student1=new Student();
           student1.setName("玉儿");
           //给学生分配班级
           student1.setClasses(classes);
           session.save(student1);
           Student student2=new Student();
           student2.setName("黄根儿");
           student2.setClasses(classes);
           session.save(student2);
          
          
           session.getTransaction().commit();
       }catch(Exception e){
           e.printStackTrace();
           session.getTransaction().rollback();
       }finally{
           HibernateUtils.closeSession(session);
       }
    }
 
执行后在显示SQL为:
 
先保存班级
insert into t_classes (name) values (?)
 
再存学生,因为学生知道自己是哪班的。因为先建立的班级。为什么知道?因为先建立的关系。用student1.setClasses(classes)就知道这个学生所在的班级了,给它设上了,玉儿就是理想Database实验室的。
insert into t_student (name, classesid) values (?, ?)
insert into t_student (name, classesid) values (?, ?)
 
八、总结:所以采用一对多关系映射时,都在多的一端来维护。
1、第七个步骤是先存班级,再存学生。那么我们像一对多单向关联映射那样,如果先存学生,再存班级,可不可以?当然还是可以的。因为Class及Classes.hbm.xml 文件没有改变呀。
所以如果执行testSave2()方法:
 

 public void testSave2(){
  Session session=null;
  try{
   session=HibernateUtils.getSession();
   session.beginTransaction();
   
   //先建立学生对象集合
   Student student1=new Student();
   student1.setName("菜10");
   session.save(student1);
   Student student2=new Student();
   student2.setName("容祖儿");
   session.save(student2);
   
   //用泛型了
   //因为班级里的学生是一个集合,所以要构造一个集合出来
   Set<Student>  students=new HashSet<Student>();
   //向集合里面加数据
   students.add(student1);
   students.add(student2);
     
   //要知道哪个学生所在的班级,要new 班级出来
   Classes classes=new Classes();
   classes.setName("尚学堂");
   //这个班级里面有哪些学生,要用set方法加上去。
   //这样这个尚学堂班级里面就有两个学生了
   classes.setStudents(students);
   session.save(classes);
   
   session.getTransaction().commit();
  }catch(Exception e){
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally{
   HibernateUtils.closeSession(session);
  }
 }
 
执行后,数据也可保存上去。数据库中显示存入后的数据
mysql> select * from t_classes;
+----+--------------------+
| id | name               |
+----+--------------------+
1 | 尚学堂             |
2 | 理想Database实验室 |
3 | 尚学堂             |
+----+--------------------+
3 rows in set (0.00 sec)
mysql> select * from t_student;
+----+--------+-----------+
| id | name   | classesid |
+----+--------+-----------+
1 | 菜10          1 |
2 | 容祖儿 |         1 |
3 | 玉儿          2 |
4 | 黄根儿 |         2 |
5 | 菜10          3 |
6 | 容祖儿 |         3 |
+----+--------+-----------+
6 rows in set (0.00 sec)
 
2、所以如果想让多的一端来维护,那么原本由一的一端来维护的,要让一的一端来维护的失消。
在Classes.hbm.xml文件中,默认反转为false。就是可以在一的一端来维护的。在此<set name=”students” inverse=”true”>
就是在set标签中设置反转为true。就是反转了,反给别人了,使一的一端失效。
3、这样设置完之后,如果再执行testSave2(),就是先存学生,此时学生的classesid字段的值为空。然后再存班级,然后由班级更新学生的classesid字段。这种反转是关系的反转。
执行后,在控制台产生三条insert语句,如下:
insert into t_student (name,classesid) value (?,?)当存学生时,classesid字段值肯定为null.
insert into t_student (name,classesid) value(?,?)
insert into t_classes (name) value(?)
也就是说,t_classes表没有发出update语句来更新t_student表的classesid字段的值。因为我们已经设置为反转了,不让一的一端来维护关系了,一的一端就不管了,所以update语句就不发了。虽然可以保存,但是在一的一端来维护的关系失效了,也就是说,班级根本不知道有学生存在。
 
MYSqL数据库显示如下:
mysql> select * from t_classes;
+----+--------------------+
| id | name               |
+----+--------------------+
1 | 尚学堂             |
2 | 理想Database实验室 |
3 | 尚学堂             |
4 | 尚学堂             |
+----+--------------------+
5 rows in set (0.00 sec)
mysql> select * from t_student;
+----+--------+-----------+
| id | name   | classesid |
+----+--------+-----------+
1 | 菜10          1 |
2 | 容祖儿 |         1 |
3 | 玉儿          2 |
4 | 黄根儿 |         2 |
5 | 菜10          3 |
6 | 容祖儿 |         3 |
7 | 菜10       NULL |
8 | 容祖儿 |      NULL |
+----+--------+-----------+
也就是说,执行结果如下:数据可以正常保存,但是关系字段为null.
 
九、所以当一的一端将关系设置为反转后,只能在多的一端来维护。
就是说,先new出classes,然后将其分配到学生里面。
在多的一端维护的好处是,不用发出update。因为学生知道自己是哪个班级的了。
而且,为什么在多的一端维护关系比较好?因为关系字段就在多的一端,就在student这个表里。当然谁离的近,谁维护好了。
 
十、readme. txt
hibernate 一对多关联映射双向。(双向关联 Classed<----->Student)
一对多双向关联映射:
    *在一一端的集合上使用<key>,在对方表中加入一个外键指向一一端
    *在多一端采用<many-to-one>
   
注意:<key>标签指定的外键字段必须和<many-to-one> 指定的外键字段一致,否则引用字段的错误
 
如果在“一”一端维护一对多关系, hibernate会发出多余的update 语句,所以我们一般在多的一端来维护关联关系
 
关于invserse属性:
    inverse主要用在一对多和多对多双向关联上, inverse可以被设置到集合标签<set> 上,
    默认inverse为false,所以我们可以从“一”一端和“多”一端维护关联关系。
         如果设置成inverse为true,则我们只能从多一端来维护关联关系
   
    注意:inverse属性,只影响数据的存储,也就是持久化
   
 inverse和cascade
    *inverse是关联关系的控制方向
    * cascade操作上的连锁反应
 
十一
测试加载:加载学生,看一下能不能把学生对应的班级加载上来。
testLoad3()测试方法如下:

 public void testLoad3(){
  Session session=null;
  try{
   session=HibernateUtils.getSession();
   
   Student student=(Student)session.load(Student.class,4);
   System.out.println(student.getName());
   System.out.println(student.getClasses().getName());
   session.beginTransaction();
   session.getTransaction().commit();
  }catch(Exception e){
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally{
   HibernateUtils.closeSession(session);
  }
 }
 
 
生成的SQL语句:
select student0_.id as id1_0_, student0_.name as name1_0_, student0_.classesid as classesid1_0_ from t_student student0_ where student0_.id=?
select classes0_.id as id0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.id=?
输出的数据:
黄根儿
理想Database实验室
 
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值