热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

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

建立对SQL语句的映射是Hibernate框架操作数据库的主要手段,这里我们列举实例来为大家讲解Java的Hibernate框架中一对多的单向和双向关联映射

一、一对多单向关联映射

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

201662390330145.png (450×131)

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

201662390408017.png (594×204)

1、基本配置

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

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

<&#63;xml version="1.0"&#63;> 
 
 
   
     
       
     
     
     
       
       
     
   
 

对应的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) 

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

201662390506200.png (592×298)

对比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); 
  } 
} 

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

201662390557907.png (677×392)

(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=&#63; 
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=&#63; 
student.name=lisi 
student.name=zhangsan 

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

201662390642632.png (450×131)

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

201662390706339.png (649×213)

在一对多的单向关联中映射文件只需要在一的一端进行特殊配置就可以,使用配置,并在对象模型中使用set迭代器来设置外联的对象模型,但是不同的是在双向的关联中需要在多的一端添加对应的另一端的外键关联,这时候就必须在多的一端使用的关联关系来标明这种双向性。

1、映射

这里还使用Classes和Student来做示例,在Classes一端的内容和上文相同不会发生变换,但是多的一端Student的配置会发生变化,也就是在映射文件中需要添加标签。
Student.hbm.xml映射文件配置需要添加外键列标签,并且该列的名称要和Classes.hbm.xml的外键列的名称一致,具体如下代码:

<&#63;xml version="1.0"&#63;> 
 
 
   
     
       
     
     
     
     
   
 

Classes.hbm.xml映射文件的配置和上篇文章相同,需要注意的是在Classes.java文件中添加了set属性映射对应了Student对象,所以在映射文件中需要添加set标签来指示为对象模型中使用了set迭代器,具体配置如下代码:

<&#63;xml version="1.0"&#63;> 
 
 
   
     
       
     
     
     
       
       
     
   
 

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); 
  } 
} 

对应的写入数据库中的信息列表如下图:
201662390812018.png (603×268)

3.2 先写学生后写班级
先把学生写入到数据库中此时因为学生表需要获取对应的班级列的主键信息,但是因为班级信息转化到Transient状态,所以在写入学生信息时会有null值,代码如下:

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

201662390857957.png (617×308)

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

通过学生加载班级信息Classes.id= 1 


推荐阅读
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 构建基于BERT的中文NL2SQL模型:一个简明的基准
    本文探讨了将自然语言转换为SQL语句(NL2SQL)的任务,这是人工智能领域中一项非常实用的研究方向。文章介绍了笔者在公司举办的首届中文NL2SQL挑战赛中的实践,该比赛提供了金融和通用领域的表格数据,并标注了对应的自然语言与SQL语句对,旨在训练准确的NL2SQL模型。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 本文探讨了 RESTful API 和传统接口之间的关键差异,解释了为什么 RESTful API 在设计和实现上具有独特的优势。 ... [详细]
  • 使用C#开发SQL Server存储过程的指南
    本文介绍如何利用C#在SQL Server中创建存储过程,涵盖背景、步骤和应用场景,旨在帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 本文探讨了适用于Spring Boot应用程序的Web版SQL管理工具,这些工具不仅支持H2数据库,还能够处理MySQL和Oracle等主流数据库的表结构修改。 ... [详细]
  • 本文详细介绍了如何通过多种编程语言(如PHP、JSP)实现网站与MySQL数据库的连接,包括创建数据库、表的基本操作,以及数据的读取和写入方法。 ... [详细]
  • 在使用 DataGridView 时,如果在当前单元格中输入内容但光标未移开,点击保存按钮后,输入的内容可能无法保存。只有当光标离开单元格后,才能成功保存数据。本文将探讨如何通过调用 DataGridView 的内置方法解决此问题。 ... [详细]
  • 本文详细介绍了如何在 Linux 平台上安装和配置 PostgreSQL 数据库。通过访问官方资源并遵循特定的操作步骤,用户可以在不同发行版(如 Ubuntu 和 Red Hat)上顺利完成 PostgreSQL 的安装。 ... [详细]
  • 如何在PostgreSQL中查看数据表
    本文将指导您使用pgAdmin工具连接到PostgreSQL数据库,并展示如何浏览和查找其中的数据表。通过简单的步骤,您可以轻松访问所需的表结构和数据。 ... [详细]
  • 利用存储过程构建年度日历表的详细指南
    本文将介绍如何使用SQL存储过程创建一个完整的年度日历表。通过实例演示,帮助读者掌握存储过程的应用技巧,并提供详细的代码解析和执行步骤。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 在使用SQL Server进行动态SQL查询时,如果遇到LIKE语句无法正确返回预期结果的情况,通常是因为参数传递方式不当。本文将详细探讨这一问题,并提供解决方案及相关的技术背景。 ... [详细]
  • 本文介绍如何通过创建替代插入触发器,使对视图的插入操作能够正确更新相关的基本表。涉及的表包括:飞机(Aircraft)、员工(Employee)和认证(Certification)。 ... [详细]
author-avatar
M-When
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有