上篇文章讨论了单向关联的一对多映射,在一的一端维护双向的关系这样的做法尽管能实现可是存在非常多缺陷,首先生成非常多多余的SQL语句,由于多的一端不维护关系,仅仅有一的一端维护,在进行操作时一的一端会发出多余的update语句;其次,由于多的一端不知道一的一端存在,所以在保存多的一端时假设外键为null值,而且在设计数据库时关系字段设为非空,则将无法保存数据。由于单向关联一对多存在非常多缺点那就没有其他的办法了吗,能够採用双向关联来优化。

一、一对多双向关联


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

相应的关系模型图没有太大的变化,由于它们之间的关系是双向的,所以在关系模型中两端同一时候维护关联关系,映射到关系模型中例如以下图所看到的:

在一对多的单向关联中映射文件仅仅须要在一的一端进行特殊配置就能够,使用<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、读取操作


     相对于写入数据而言,读取数据就变得非常easy了,由于是双向的关联所以数据的读取也是双向的,能够从不论什么一端读取还有一端的信息,例如以下代码:
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


结语

        双向的一对多讨论完毕,假设在使用一对多关系时建议使用双向的关联关系,它能够优化关系的类型,并且也能够保证在写入时不会出错。总结两篇文章,单向和双向性事实上是通过使用<one-to-many>和<many-to-one>来实现的,前者重在设置关联关系,并不会生成新列,可是后者在生成关联关系的同一时候会生成新列。

【Hibernate步步为营】--(一对多映射)之双向关联的更多相关文章

  1. hibernate一对一外键双向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  2. hibernate一对一主键双向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  3. Hibernate一对一外键双向关联(Annotation配置)

    如上图所示:一个学生有一个学生证号,一个学生证号对应一名学生.在Hibernate中怎么用Annotation来实现呢? 学生类,主键是id:学生证的主键也是Id: Student.java pack ...

  4. java之hibernate之多对多双向关联映射

    1.比如在权限管理中,角色和权限之间的关系就是多对多的关系,表结构为: 2.类结构 Role.java public class Role implements Serializable{ priva ...

  5. hibernate多对一双向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  6. Hibernate多对多双向关联的配置

    Hibernate的双向多对多关联有两种配置方法:那我们就来看看两种方案是如何配置的.  一.创建以各自类为类型的集合来关联 1.首先我们要在两个实体类(雇员<Emploee>.工程< ...

  7. hibernate一对一外键单向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  8. hibernate一对一主键单向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  9. 【Hibernate步步为营】--(一对多映射)之单向关联

    上篇文章讨论了双向关联的一对一映射,用了两个章节,主要是从主键和外键两种关联映射展开具体讨论.双向关联的映射须要在两个映射文件里分别加入相互的相应关系.斌刚在相应的类中加入相应的关联类的属性.这样在一 ...

随机推荐

  1. MySQL 触发器结构及三个案例demo

    --你必须拥有相当大的权限才能创建触发器(CREATE TRIGGER),如果你已经是Root用户,那么就足够了.这跟SQL的标准有所不同. CREATE TRIGGER语法 CREATE TRIGG ...

  2. Android - 通过Intent启动Activity

    通过Intent启动Activity 本文地址: http://blog.csdn.net/caroline_wendy 为了动态关联Activity界面,使用Intent启动.能够灵活绑定. 在In ...

  3. LB 负载均衡的层次结构(转)

    作为后端应用的开发者,我们经常开发.调试.测试完我们的应用并发布到生产环境,用户就可以直接访问到我们的应用了.但对于互联网应用,在你的应用和用户之间还隔着一层低调的或厚或薄的负载均衡层软件,它们不显山 ...

  4. Thinkpad X200 屏幕备案

    妈妈蛋,屏幕废物前几天(闪屏->暗->变暗),因此,它只能监视房外 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjk2NTg5MA= ...

  5. Trie图

    AC自动机是KMP的多串形式,当文本串失配时,AC自动机的fail指针告诉我们应该跳到哪里去继续匹配(跳到当前匹配串的最长后缀去),所以AC自动机的状态是有限的 但是AC自动机具有不确定性, 比如要求 ...

  6. malloc功能具体解释

    一.原型:extern void *malloc(unsigned int num_bytes); 头文件:#include <malloc.h> 或 #include <alloc ...

  7. Django操作model时刻,一个错误:AttributeError:’ProgrammingError’ object has no attribute ‘__traceback__’

    原因:在Django项目下对应的应用以下的models.py配置的model(也就是class)没有创建成对应的表. 这是怎么回事呢? 首先,将models.py里面的model创建成相应的数据库表的 ...

  8. BC 2015在百度之星程序设计大赛 - 预赛(1)(系列转换-二分法答案贪婪)

    系列转换 Accepts: 816 Submissions: 3578 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 ...

  9. hive load from hdfs出错

    使用hive load从hdfs中load data的时候,hiveql如下: load data inpath 'hdfs://192.168.0.131:9000/hive/test.log' o ...

  10. 使用CMakeLists.txt 判断编译器是否支持C++11

    #将下面的内容添加到CMakeLists.txt当中include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11&quo ...