Java的Hibernate框架中一对多的单向和双向关联映射

一、一对多单向关联映射

一对多关系的对象模型在日常生活中也经常看到,就拿学生和班级来说,一个班级里有多个学生,所以班级和学生的关系是一对多的关系,映射到对象模型中,如下图:

对象模型说明了这种一对多的关系是由一的一端来维护的,那么映射成关系模型就是一个班级字段下面会有多个学生,这样就形成了一对多的关系,通过班级能够查询获得学生信息,对应的关系模型如下图:

1、基本配置

有了对象模型接下来就让它们映射为对应的关系代码,在进行关系映射时需要在一的一端添加<one-to-many>标签,另外还需要在一的一端添加Set属性,它支持延迟加载,然后在映射文件添加set标签,并指明一对多的关系,这样就能够在一的一端查询获取多的一端。

Classes类及映射文件:
它是模型中最重要的一端,在该端需要添加对应的set属性,并在配置文件中添加set标签,在set标签中配置相应的<one-to-many>对象,具体Classes.java对象代码如下:

package com.src.hibernate; 

import java.util.Set; 

public class Classes {
  private int id;
  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;
  }
  private String name; 

  //Set支持延迟加载
  private Set students;
  public Set getStudents() {
    return students;
  }
  public void setStudents(Set students) {
    this.students = students;
  }
}

Classes对象中使用了set属性,但是只是说明了延迟加载的属性,并没有为属性配置对应的对象,属性的对象是要在映射文件中来配置的,需要添加set标签,并在set标签中添加<one-to-many>标签,具体如下代码:

<?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.hibernate.Classes" table="t_classes">
    <id name="id">
      <generator class="native"/>
    </id>
    <property name="name"/>
    <set name="students">
      <key column="classesid"></key>
      <one-to-many class="com.hibernate.Student"></one-to-many>
    </set>
  </class>
</hibernate-mapping>

对应的Student对象中的代码和映射文件不需要什么特殊的配置,只需要按照通常的写法编写即可,具体的配置方法不再详述,很简单。配置好后需要生成对应的SQL语句,将对象模型转化为关系模型时Hibernate生成相应的语句如下:

alter table t_student drop foreign key FK4B9075705E0AFEFE
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 FK4B9075705E0AFEFE (classesid), add constraint FK4B9075705E0AFEFE foreign key (classesid) references t_classes (id)

生成的对应的关系模型如下图:

对比SQL语句和关系模型,相应的表之间的关联是通过外键来维护的,首先是创建两张表,并指定表的主键,最后添加一对多的外键关联关系。

2、基本操作
在对数据库的操作无非是读和写两种,修改也属于写的一种,接下来看看是如何向数据库中写入和读取操作的。

(1)写入数据:
写入数据需要注意的是一对多的关系,所以在添加的时候需要添加多个学生类,另外由于在classes中添加了对应的set属性,所以在添加Student对象时应该使用HashSet来添加,这样既可实现一对多的关系,具体如下代码:

public void testSave2(){
  Session session=null;
  try{
    session=HibernateUtils.getSession();
    session.beginTransaction(); 

    Student student1=new Student();
    student1.setName("zhangsan");
    session.save(student1); 

    Student student2=new Student();
    student2.setName("lisi");
    session.save(student2); 

    Classes classes=new Classes();
    classes.setName("ClassOne"); 

    Set students=new HashSet();
    students.add(student1);
    students.add(student2); 

    classes.setStudents(students);
    //可以成功保存数据
    //但是会发出多余的update语句来维持关系,因为是一对多的原因
    session.save(classes);
    session.getTransaction().commit();
  }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally{
    HibernateUtils.closeSession(session);
  }
}

那么运行上面的测试用例生成的对应的数据写入到数据库中后如下图:

(2)读取数据:
写入操作相对简单,只需要把所有加载的对象都添加到Transient状态下,运行相应的方法就可以插入内容,但是对应的读取操作就会稍微复杂点,因为需要迭代获取所有的学生对象,所以这种一对多的关系效率并不很高,具体代码如下:

package com.test.hibernate; 

import java.util.Iterator;
import java.util.Set;
import com.src.hibernate.*;
import junit.framework.TestCase;
import org.hibernate.Session; 

public class One2ManyTest extends TestCase {
  public void testLoad1(){
    Session session=null;
    try{
      session=HibernateUtils.getSession();
      session.beginTransaction(); 

      //获取主键为5的班级信息
      Classes classes=(Classes)session.load(Classes.class,5);
      //打印班级信息
      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());
      } 

      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=ClassOne
Hibernate: select students0_.classesid as classesid1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_ from t_student students0_ where students0_.classesid=?
student.name=lisi
student.name=zhangsan

二、一对多双向关联映射
这里继续采用学生和班级作为示例,班级和学生之间是一对多的关系,一个班级中拥有多名学生,和上篇文章不同的是这里的关系是双向的,也就是一的一端和多的一端同时维护关联关系,所以它的对象图如下:

对应的关系模型图没有太大的变化,因为它们之间的关系是双向的,所以在关系模型中两端同时维护关联关系,映射到关系模型中如下图所示:

在一对多的单向关联中映射文件只需要在一的一端进行特殊配置就可以,使用<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值,代码如下:

写入后对应的数据库视图如下:

对比两种写入操作,因为两个写入的先后顺序不同所以出现了不同的结果,但因为是双向的关联关系所以在写入时并不会发生异常。
   
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

(0)

相关推荐

  • 举例讲解Java的Hibernate框架中的多对一和一对多映射

    多对一(Many-to-One)映射 多对一(many-to-one)关联是最常见的关联关系,其中一个对象可以与多个对象相关联.例如,一个相同的地址对象可以与多个雇员的对象相关联. 定义RDBMS表: 考虑一个情况,我们需要员工记录存储在EMPLOYEE表,将有以下结构: create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20

  • java Hibernate 一对多自身关联问题

    Hibernate 一对多自身关联问题 这个很难描述清楚,只能引用CSDN中我提问的帖子了: http://topic.csdn.net/u/20080711/16/7494bf10-48ca-4b2e-8a01-303e647f5516.html 方法,在表单中取得一个PO,然后session.save(po),如下: 程序代码             tx = session.beginTransaction();         session.save(catalog);        

  • 深入解析Java的Hibernate框架中的一对一关联映射

    作为一个ORM框架,hibernate肯定也需要满足我们实现表与表之间进行关联的需要.hibernate在关联方法的实现很简单.下面我们先来看看一对一的做法:  不多说了,我们直接上代码:  两个实体类,TUser和TPassport: public class TUser implements Serializable{ private static final long serialVersionUID = 1L; private int id; private int age; priva

  • hibernate一对多关联映射学习小结

    一对多关联映射  映射原理  一对多关联映射和多对一关联映射的映射原理是一致的,都是在多的一端加入一个外键,指向一的一端.关联关系都是由多端维护,只是在写映射时发生了变化. 多对一和一对多的区别 多对一和一对多的区别在于维护的关系不同: (1)多对一:多端维护一端的关系,在加载多端时,可以将一端加载上来. (2)一对多:一端维护多端的关系,在加载一端时,可以将多端加载上来. 分类 一对多单向关联映射 对象模型 从对象模型中,我们可以看出,Group持有User的一个引用.由于是单向关联,所以数据

  • Hibernate一对多关联双向关联代码实现分享

    1.创建实体类(Customer.java.Orders.java) 复制代码 代码如下: package wck.stu.vo.oneToMany_single; import java.util.HashSet;import java.util.Set; public class Customer {    private String id = ""; private String cName = ""; private String bank = "

  • Hibernate映射解析之关联映射详解

    Hibernate中的关联映射 关联关系 平时开发中,类与类之间最普遍的的关系就是关联关系,而且关联是有方向的. 以部门(Dept)和员工(Employee)为例:一个部门下有多个员工,而一个员工只能属于一个部门. 从Employee到Dept的关联就是 多对一 关联. 这就说明 每个Employee对象只会引用一个Dept对象,因此在Employee类中应该定义一个Dept类型的属性,来引用所关联的Dept对象. 从Dept到Employee的关联就是 一对多 关联.这就说明 每个Dept对象

  • 详解hibernate双向多对多关联映射XML与注解版

    双向多对多关联映射原理: 假设,一个员工可能有多个角色,一个角色可能有多个员工,从员工或角色的角度看,这就是多对多的关系,不管从哪一个角度看,都是多对多的联系.多对多关联映射关系一般采用中间表的形式来实现,即新增一种包含关联双方主键的表.实现多对多关联关系,在数据库底层通过添加中间表指定关联关系,而在hibernate框架在双方的实体中添加一个保存对方的集合,在双方的映射文件中使用<set>元素和<many-to-many>元素进行关联关系的配置. 如下图所示: (1)XML版 R

  • Java的Hibernate框架中一对多的单向和双向关联映射

    一.一对多单向关联映射 一对多关系的对象模型在日常生活中也经常看到,就拿学生和班级来说,一个班级里有多个学生,所以班级和学生的关系是一对多的关系,映射到对象模型中,如下图: 对象模型说明了这种一对多的关系是由一的一端来维护的,那么映射成关系模型就是一个班级字段下面会有多个学生,这样就形成了一对多的关系,通过班级能够查询获得学生信息,对应的关系模型如下图: 1.基本配置 有了对象模型接下来就让它们映射为对应的关系代码,在进行关系映射时需要在一的一端添加<one-to-many>标签,另外还需要在

  • Java的Hibernate框架中集合类数据结构的映射编写教程

    一.集合映射 1.集合小介 集合映射也是基本的映射,但在开发过程中不会经常用到,所以不需要深刻了解,只需要理解基本的使用方法即可,等在开发过程中遇到了这种问题时能够查询到解决方法就可以了.对应集合映射它其实是指将java中的集合映射到对应的表中,是一种集合对象的映射,在java中有四种类型的集合,分别是Set.Map.List还有普通的数组,它们之间有很大的区别: (1)Set,不可以有重复的对象,对象是无序的: (2)List,可以与重复的对象,对象之间有顺序: (3)Map,它是键值成对出现

  • 深入解析Java的Hibernate框架中的持久对象

    一.持久对象生命周期 应用程序在使用Hibernate框架后,创建的持久对象会经历一整套生命周期来完成数据库的操作,其中主要的三个状态分别是瞬态(Transient).持久化(Persistent).脱管(detached).这三种状态的转换是能够在应用程序中控制的,如下图: 为了能清楚的了解这几种状态,这里使用一个实例来查看下这几种状态下对象的不同,下面状态内的代码,具体步骤如下: (1)创建Hibernate_session程序集,并添加像相应的jar包: (2)配置Hibernate,添加

  • Java的Hibernate框架中的双向主键关联与双向外键关联

    一.双向主键关联 双向的主键关联其实是单向一对一主键关联的一种特殊情况,只不过要在关联对象的两端的映射文件中都要进行<one-to-one>的配置,另外还要在主映射的主键一端采用foreign外键关联属性. 这里同样使用Person和IdCard来讨论,一个人对应着一个唯一的身份证,而且一个身份证也唯一映射着一个人,所以这就产生了双向的关联关系,Person的主键同样也是IdCard的主键,分别是主键的同时也是外键,这种关联关系成为双向一对一映射,表现到关系模型中可如下图: 图中的两个表采用了

  • 详解Java的Hibernate框架中的Interceptor和Collection

    Interceptor 讲到Interceptor,相信熟悉struts2的童鞋肯定不会陌生了,struts2可以自定义拦截器进行自己想要的一系列相关的工作.而这里我们说的Interceptor也是差不多相似的功能.  废话不说,直接来代码:  下面这个是MyInterceptor类,它实现了Interceptor接口: public String onPrepareStatement(String arg0) { return arg0; } public boolean onSave(Obj

  • 浅析Java的Hibernate框架中的缓存和延迟加载机制

    hibernate一级缓存和二级缓存的区别 缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能.缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. 缓存的介质一般是内存,所以读写速度很快.但如果缓存中存放的数据量非常大时,也会用硬盘作为缓存介质.缓存的实现不仅仅要考虑存储的介质,还要考虑到管理缓存的并发访问和缓存数据的生命周期. Hibernate的缓存包括Sessi

  • Java的Hibernate框架中的组合映射学习教程

    一.组合映射 组合是关联关系的一种特殊情况,是关联关系耦合度最高的一种关系,组合的主对象和子对象拥有相同的生命周期,主对像消亡的话子对象也会消亡.这里使用雇主和用户作为示例,用户和雇主都拥有联系方式属性,如果这里站在对象角度思考的话,常常会把对象模型绘制成为组合的方式,抽象出来一个共同的联系方式类,然后两种人分别包含相应的联系方式对象即可,向应的对象模型时它的对象示例如下图所示: 组合对象模型在生成相应的关系模型后会把对应的子类包含到主表中,所以对应的表结构会将相应的属性生成到对应的表中,相应的

  • Java的Hibernate框架中用于操作数据库的HQL语句讲解

    上次我们一起学习了用Criteria进行相关的操作,但由于Criteria并不是Hibernate官方推荐的查询方式,我们也并不多用.现在我们来看一下官方推荐的HQL,一起学习一下它的强大.  说是HQL,也就是Hibernate查询语句,和SQL有什么区别呢?一个字母的区别,哈哈.  当然不是这样,HQL和SQL的区别在于思想的不同,HQL是用面向对象的方向进行查询,而SQL则是对数据库二维表进行查询,这里包含的是思想的不同.HQL实际上也是SQL,它由Hibernate帮我们在内部进行转换,

  • 详解Java的Hibernate框架中的set映射集与SortedSet映射

    Set 集合Set是一个java集合不包含任何重复的元素.更正式地说,Set不包含任何元素对e1和e2,使得e1.equals(e2),和至多一个空元素.所以被添加到一组对象必须实现equals()和hashCode()方法,使Java可以判断任何两个元素/对象是否是相同的. 集被映射到与映射表中<set>元素,并在java.util.HashSet中初始化.可以使用Set集合在类时,有一个集合中不需要重复的元素. 定义RDBMS表: 考虑一个情况下,我们需要我们的员工记录存储在EMPLOYE

随机推荐