第三章Hibernate关联映射

一、关联关系

类与类之间最普通的关系就是关联关系,而且关联是有方向的。

以部门和员工为列,一个部门下有多个员工,而一个员工只能属于一个部门,从员工到部门就是多对一关联。

Hibernate关联映射的作用:避免了在对象模型和关系数据模型之间的切换。

缺陷:hibernate不是适合数据链比较多的操作,比如删除外键的关联对象,它要一条一条的删除,效率不高。

1.1建立单项多对一关联关系

     以区县级(District)和街道(Street)为例,介绍如何建立单向多(街道)对一(区县)关联关系。

         第一步:建立持久化实体类,映射关系是多(街道)对一(区县),因此只用在街道类中加入所属的(区县)对象即可。

public class Street {
    private int id;//街道编号
   
private String
name;//街道名称
   
private District
district;//所属区县

}

第二步:编写映射文件Street.hbm.xml

<?xml
version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="jbit.entity.Street" table="street">
        <!--绑定主键-->
       
<id name="id" column="street_id" >
            <generator class="increment">
            </generator>
        </id>
        <property name="name"
column="name" />

<!--many-to-one 包括以下属性

  1. 1.      
    name:设定持久化类的属性名,此处为Street类的district;//所属区县
  2. 2.      
    column:设定持久化类的属性对应的表的外键,此处为street表的外键district_id
  3. 3.      
    class:设定持久化类的属性的类型,此处设定district属性为District类型

-->
        <many-to-one name="district"
class="jbit.entity.District" column="district_id" lazy="proxy" /><!--district_id外键-->
   
</class>
</hibernate-mapping>

         此时,单向的多对一映射配置就完成了。

1.2建立双向一对多关联关系

当类与类之间建立了关联,就可以方便的从一个对象导航到另一个对象,或者通过集合导航到另一组对象。

在面向对象语言编写的程序中,通过关联关系从一个对象导航到另一个对象显然比通过编码到数据库中查询来的更加自然,且无需额外的编码。并且基于关联关系,在增、删、改操作中还可以对相关对象实现自动化的级联处理,同样减少编码工作,提高开发效率。

前面已经建立了单向多(街道)对一(区县)关联关系,下面再增加一(区县)对多(街道)关联,区县类和街道类之间就构成了双向的关联,即双向一对多关联。

第一步:建立持久化实体类,映射关系是一(区县)对多(街道)。

public class District {
    private int id;//区县编号
   
private String name;//区县名称
   
private Set<Street> street=new HashSet<Street>();//街道,常见错误,封装的时候new一个,避免空指针异常
}

在上面使用Set<>集合时因为它是无序的集合,而List<>是有序的,访问需要下标,所以用Set<>.

第二步:编写映射文件District.hbm.xml

<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="jbit.entity.District" table="district">
        <!--绑定主键-->
       
<id name="id" column="district_id" >
            <generator class="increment">
            </generator>
        </id>
        <property name="name"
column="name" />
        <!--cascade=" save-update,delete,merge "关联持久化操作支持修改删除和保存,不建设使用all-->
        <!--inverse="true"
被动方维护关联关系, 护关联关系一般给多对一,一的那一方-->
        <!--lazy="extra"
增强延迟加载-->
       
<set name="street" cascade="save-update,delete,merge" inverse="true" lazy="extra">
            <key column="district_id"
/>
            <one-to-many class="jbit.entity.Street"
/>
        </set>

<!-- set元素的属性:

1.key: column属性设定与所关联的持久化类相对应的表的外键,此处为street表的外键district_id

2.ont-to-many:class属性设定所关联的持久化类型,此处为Street类

3.set:表明District类的street属性为jbit.entity.Street集合类型

-->
    </class>
</hibernate-mapping>

1.3双向关联关系下的增删改操作

关联关系除了可以通过对象间的导航实现相关的对象的自动检索外,还可以在对象的增删改操作中,对相关对象实现自动化的级联处理,而无序人工进行相关编码,从而提高了效率。

级联的操作细节可以在持久化类的映射文件中通过cascade属性和inverse属性进行控制。

1.cascade属性

Cascade属性的部分可选值

cascade属性值

描述

none

当session对象操纵当前对象时,忽略其他的关联对象,cascade的默认值。

save-update

通过session的save(),update()和saveOrUpdate()的方法,保存所有关联的瞬时状态的对象,

并且级联更新所有关联的游离状态的对象。

delete

当通过session的delete()方法时,会级联删除所有关联的对象

all

包含所有的行为,出于安全性的考虑,一般避免使用

marge

执行session的marge()方法

<set name="street" cascade="save-update,delete,merge"> cascade属性可以赋值多个,用逗号隔开。

cascade建议用在一对多或者多对一,一的一方,避免不必要的麻烦。

2.<set>元素的inverse属性

“inverse”意思为“反转”,在Hibernate中,inverse属性指定了关联关系中的方向。

inverse属性值有两个,即true和false,默认时false。就是不反转,就是不交出维护外键的权力,因此会执行相关对象关联的外键的HQL语句。从而保证数据的可靠性。

如果设置成true,则不会再执行相关的修改外键的HQL语句。在编码中必须建立对象的双向关联关系。

操作示例:

         要求:1.添加区县的同时添加两条街道;2.设置inverse的属性为true,从某区县中移走一条街道。

         public class DistrictBiz {
    /**
     *
增加区县信息
     */
   
public
Serializable
saveDistrict(District district){
        Session session= Util.openSession();
        Transaction
transaction=session.beginTransaction();
        Serializable save = null;
        try {
            save=session.save(district);
            transaction.commit();
        }catch
(Exception e){
            transaction.rollback();
        }finally
{
            Util.closeSession(session);
        }
        return
save;
    }

/**
     *
修改街道信息
     * @param district
    
*/
   
public
void
updateDistrict(District
district){
        Session session= Util.openSession();
        Transaction
transaction=session.beginTransaction();
        try {
            session.update(district);
            transaction.commit();
        }catch
(Exception e){
            transaction.rollback();
        }finally
{
            Util.closeSession(session);
        }
    }

}

测试:

         public class Test2 {
    public
static void
main(String[] args) {
        //1.添加区县的同时添加两条街道;

        DistrictBiz
biz=new DistrictBiz();
        // 创建对象
       
District
district=new District();
        district.setName("盐田区");
        Street street1 = new Street( "大梅沙街道");
        Street street2 = new Street("梧桐山街道");
        //绑定双向关系
       
district.getStreet().add(street1);
        street1.setDistrict(district);
       
district.getStreet().add(street2);
        street2.setDistrict(district);
        //执行增加
       
Serializable
serializable = biz.saveDistrict(district);
        System.out.println("********新增加区县信息"+serializable);
        System.out.println(district+"\n");
        Iterator
iterator=district.getStreet().iterator();
        while
(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //2.从某区县中移走一条街道
       
street1.setDistrict(null);
        district.getStreet().remove(street1);
        biz.updateDistrict(district);
        System.out.println("********移出区县的街道后:");
        System.out.println(district+"\n");
        Iterator
iteratoraa=district.getStreet().iterator();
        while
(iteratoraa.hasNext()){
            System.out.println(iteratoraa.next());
        }
    }
}

 

1.4建立多对多关系

多对多关系除了两张“多”方的表之外,还需要一张而外的表,即关系表,通过外键分别引用两张“多”方的主键来实现多对多的关联。

下面通过项目表(Project)和员工表(Employee),一个项目对应多个员工,一个员工参与多个项目,双向多对多来示例:
第一步:编写持久化化类Project,和其映射文件。

public class Project {
     private Integer
proid;
     private String
proname;
     private Set<Employee>
employees=new
HashSet<Employee>();

}

<?xml
version="1.0" encoding="UTF-8"?><!—映射文件-->
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="jbit.entity.Project" table="PROJECT">
       
<!--绑定主键-->
       
<id name="proid" column="PROID" >
            <generator class="increment">
            </generator>
        </id>
        <property name="proname"
column="PRONAME" />
        <!--PROEMP,第三方关联关系表-->
        <!--inverse="true"
被动方维护关联关系-->
        <!--<key
column="REMPID" />
和column="RPROID都是第三方关系型表的字段-->
       
<set name="employees"   table="PROEMP"
cascade="merge,save-update ">
            <key column="RPROID" />
            <many-to-many class="jbit.entity.Employee"
column="REMPID" />
        </set>
    </class>
</hibernate-mapping>

重点:set元素的业务逻辑

  1. 1.       关系表PROEMP,(关系)它包含 REMPID和RPROID两个列外键分别关联项目表和员工表的主键。
  2. 2.       Key:根据项目编号RPROID在PROEMP表中查找关联的REMPID(员工编号)
  3. 3.       根据员工编号REMPID在员工表(Employee)中查询员工信息,类型为class: jbit.entity.Employee。

使用注意事项:

         cascade="merge,save-update
"
是合理的,不建议把cascade属性设置为”all”和”delete”,如果删除一个项目对象时,级联会删除所有与它相关的员工信息,会破坏数据库的完整性。

第二步:编写持久化化类Employee,和其映射文件。

public class Employee {
    private Integer
empid;
    private String
empname;
    private Set<Project>
projects=new
HashSet<Project>();
}

<?xml
version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="jbit.entity.Employee" table="EMPLOYEE">
        <!--绑定主键-->
       
<id name="empid" column="EMPID" >
            <generator class="increment">
            </generator>
        </id>
        <property name="empname"
column="EMPNAME" />
        <!--PROEMP,第三方关联关系表-->
        <!--inverse="true"
被动方维护关联关系-->
        <!--<key column="REMPID"
/>
和column="RPROID都是第三方关系型表的字段-->
       
<set name="projects"  inverse="true"
table="PROEMP">
            <key column="REMPID" />
            <many-to-many class="jbit.entity.Project"
column="RPROID" />
        </set>
    </class>
</hibernate-mapping>

注意事项:
1. 双向关联时,必须要有一方放弃控制反转权,即设置inverse=”teue”
2. 保存的时候,一定要保存有控制权的一方。
   第三步:多对都的持久化操作:1.添加项目的时候同时添加员工,2.把某一个员工加入到另一个项目组,3.项目结束时,把员工从该项目移走。

         public class ProjectService {
    /**
     *
保存或修改项目信息
     */
   
public
void
saveorUpdate(Project
project){
        Session session= Util.openSession();
        Transaction
transaction=session.beginTransaction();
        try {
           session.saveOrUpdate(project);
            transaction.commit();
        }catch
(Exception e){
            transaction.rollback();
        }finally
{
            Util.closeSession(session);
        }
     }
 }

public class Test3 {
    public
static void
main(String[] args) {

//1.添加项目的时候同时添加员工
        ProjectService service=new ProjectService();
        Project project=new Project();
        project.setProname("易买网");
        Employee employee=new Employee();
        employee.setEmpname("王胖子");
        Employee employee1=new Employee();
        employee1.setEmpname("邓矮子");
        //建立对象间的双向关联
       
project.getEmployees().add(employee);
       
employee.getProjects().add(project);
       
project.getEmployees().add(employee1);
       
employee1.getProjects().add(project);
        //保存对象
       
service.saveorUpdate(project);
        System.out.println("*********\n添加项目的时候添加员工");
        System.out.println(project);
        Iterator
iterator=project.getEmployees().iterator();
        while
(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //2.把某一个员工加入到另一个项目组
        Project project1=new Project();
        project1.setProname("英雄连大");
       
project1.getEmployees().add(employee);
       
employee.getProjects().add(project1);
        service.saveorUpdate(project1);
        System.out.println("********\n把第一个员工加入到另一个项目组");
        System.out.println(project1);
        Iterator
iterator1=project1.getEmployees().iterator();
        while
(iterator1.hasNext()){
            System.out.println(iterator1.next());
        }

//3.项目结束时把员工从该项目中删除
       
project1.getEmployees().remove(employee);
       
employee.getProjects().remove(project1);
        service.saveorUpdate(project1);
        System.out.println("********\n项目结束时把员工从该项目中删除");
        System.out.println(project1);
        System.out.println("项目"+project1.getProname()+"的员工数量为:"+project1.getEmployees().size());
   }
}

  多对多的总结:

  1. 1.       多对多都是set,并且使用set的时候new 一个HashSet<>()避免空指针异常
  2. 2.       通过第三张“关系表”映射
  3. 3.       需要把一方的inverse设置为true,放弃维护外键的权力
  4. 4.       可以分解成两个一对多的映射,这种情况时出现在关系表中除了外键列,还有其他的业务字段需要使用时。
 
   
   
 

二、使用MyEclipse反向工程工具映射关联关系

详细操作步骤见https://jingyan.baidu.com/article/948f59242b9d18d80ef5f956.html

在界面通过”Enable many-to-many detection”选项选择映射的形式,创建多对多,不勾选则创建两个一对多关系。

三、延迟加载

立即加载存在两大不足:

(1)、会执行不必要的查询,影响查询性能。

(2)、可能会加载大量不需要的对象,增加系统开销,浪费内存空间。

为了解决以上问题Hibernatet提供了延迟加载策略,避免加载应用程序不需要访问的对象。

Hibernate允许在对象—关系映射文件中使用lazy属性配置加载策略,并且可以分为类级关联级两个级别,分别进行控制。

Session的list()和get()方法都是立即执行,不够lazy(懒加载)的影响。

Lazy属性

级别

Lazy属性取值

类级别

<class>元素中lazy的可选值为true(延迟加载)和false(立刻加载)。默认为true,推荐使用默认值(不设置)。

一对多和多对多

关联级别

<set>元素中的lazy可选值为true(延迟加载)和extra(增强延迟加载)和false(立即加载),默认值为true,推荐使用extra(增强延迟加载)

多对一关联级别

<many-to-one>元素lazy属性的可选值为proxy(延迟加载)和no-proxy(无代理延迟加载)和false(立即加载),默认值为proxy(延迟加载),推荐使用默认值(不设置)。

注意:选择no-proxy时需要编译期间进行字节码增强操作,否则和proxy效果一样。

 

3.1类级别的查询策略

Lazy(懒加载)的控制权大于load()方法。

<class lazy=”false”>此时 load()方法效果等同于get()

如果程序加载一个持久化对象目的时为了访问它的属性,则可以采用立即加载;如果只是为了获得它的引用,则可以采用延迟加载。

3.2一对多和多对多的查询策略

推荐使用<set lazy=”extra“>增强延迟加载,如果使用延迟加载,那么在访问一个集合的个数size()属性时,Hibernate会查询出所有的集合元素,从而浪费了资源。

3.3多对一的查询策略

在<many-to-one>中设置lazy属性,一般推荐使用默认值,也就是proxy,懒加载,也就是延迟加载,用的时候再加载。

四、Open  Session In View模式

Open Session in View模式是为了解决一些数据在延迟加载(lazy懒加载)时,会话(Session)已经关闭而引发的错误。

这个模式的主要思想是:在用户进行每次请求的时候,始终保持有一个Session对象处于开启状态。

Open Session in View模式的具体实现有以下三个步骤:

一、   把Session绑定到当前的线程,要保证每次的请求中只有一个Session对象。Dao层的HibernateUtil.currentSession()方法使用SessionFactory的getCurrentSession()方法获得Session,可以保证每次的请求只有一个Session对象存在。

二、   用Fiter过滤器在请求到达时打开Session,在页面生成完毕时关闭Session。

三、   调整业务层代码,删除和会话及事务管理的相关代码,仅保留业务逻辑代码。

关键代码:

Spring中http://blog.csdn.net/fooe84/article/details/680449

OpenSessionInViewFilter在web.xml中的配置http://blog.csdn.net/zft1045911520/article/details/60466986

方法二:编写OpenSessionInViewFilter类

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import org.hibernate.HibernateException;

import org.hibernate.Session;

import org.hibernate.Transaction;

import cn.jbit.houserent.dao.HibernateUtil;

public class OpenSessionInViewFilter implements Filter {

@Override

public void destroy() {

// TODO Auto-generated method stub

}

@Override

public void doFilter(ServletRequest arg0, ServletResponse arg1,

FilterChain arg2) throws IOException, ServletException {

Session session = null;

Transaction tx = null;

try {

// 请求到达时,打开Session并启动事务

session = HibernateUtil.currentSession();

tx = session.beginTransaction();

// 执行请求处理链

arg2.doFilter(arg0, arg1);

// 返回响应时,提交事务

tx.commit();

} catch (HibernateException e) {

e.printStackTrace();

if (tx != null)

tx.rollback();

} finally {

// 关闭session

HibernateUtil.closeSession();

}

}

@Override

public void init(FilterConfig arg0) throws ServletException {

// TODO Auto-generated method stub

}

}

在web.xml中的配置

<filter>

<filter-name>openSessionInView</filter-name>

<filter-class>

Cn.jbit.util(该类全限定路径).OpenSessionInViewFilter

</filter-class>

</filter>

<filter-mapping>

<filter-name>openSessionInView</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

第三章Hibernate关联映射的更多相关文章

  1. 第六章 Hibernate关联映射

    第六章 hibernate关联映射一.本章知识点分为2部分:1.关联关系:单向多对一关联关系,双向一对多关联关系(含一对多关联关系),多对多关联关系2.延迟加载:类级别加载策略,一对多加载策略,多对一 ...

  2. (转)Hibernate关联映射——对象的三种关系

    http://blog.csdn.net/yerenyuan_pku/article/details/70148618 Hibernate关联映射——对象的三种关系 Hibernate框架基于ORM设 ...

  3. 【学习笔记】Hibernate关联映射(Y2-1-6)

    Hibernate关联映射 关联映射就是将关联关系映射到数据库里,在对象模型中就是一个或多个引用. 1.单向多对一关联 准备数据库 部门表和员工表 其中部门表有两列 部门编号和名称 员工表有三列 员工 ...

  4. Hibernate关联映射(一对多/多对多)

    版权声明:翀版 https://blog.csdn.net/biggerchong/article/details/843401053.  Hibernate关联映射上接Hibernate持久化类:h ...

  5. Hibernate关联映射关系

    Hibernate关联映射关系 一.双向一对多关联映射关系:当类与类之间建立了关联,就可以方便的从一个对象导航到另一个或另一组与它关联的对象(一对多双向关联和多对一双向关联是完全一样的) 1.1创建实 ...

  6. Oracle primary,unique,foreign 区别,Hibernate 关联映射

    Oracle primary,unique,foreign 区别 转:http://www.cnblogs.com/henw/archive/2012/08/15/2639510.html NOT N ...

  7. (转)Hibernate关联映射——一对多(多对一)

    http://blog.csdn.net/yerenyuan_pku/article/details/70152173 Hibernate关联映射——一对多(多对一) 我们以客户(Customer)与 ...

  8. Java三大框架之——Hibernate关联映射与级联操作

    什么是Hibernate中的关联映射? 简单来说Hibernate是ORM映射的持久层框架,全称是(Object Relational Mapping),即对象关系映射. 它将数据库中的表映射成对应的 ...

  9. Hibernate关联映射 映射文件的配置

    一:多对一单向关联 首先我们必须创建两个实体类 例如:Dept类 public class Dept { private Integer deptNo; private String dName; p ...

随机推荐

  1. Spring Boot初探之log4j2配置

    一.背景 下面讲在使用Spring Boot搭建微服务框架时如何配置log4j2,通过log4j2输出系统中日志信息. 二.添加log4j2的配置文件 在项目的src/main/rescources目 ...

  2. ES6学习总结一(变量;箭头函数;解构赋值)

    一.变量 var  1 可以重复声明(var a=1;var a=7;)(一开始用着会觉得限制很少,但是在大型项目会麻烦,人多嘴杂的时候定义重复了就容易出问题还不好找) 2 无法限制修改 3 没有块级 ...

  3. web前端UI框架

    分类:WEB前端 时间:2016年1月13日 目前,众多互联网公司APP都嵌入了大量的HTML5,移动端的开发越来越重视,HTML5的运用场景也越来越多了.在移动WEB开发的过程中,使用合适的移动WE ...

  4. Thinking in Java 第二章学习笔记

    Java虽基于C++,但相比之下,Java是一种更加纯粹的面向对象程序设计语言. 在Java的世界里,几乎一切都是对象,而Java中的全部工作则是定义类,产生那些类的对象,以及发送消息给这些对象. 尽 ...

  5. WebService就是这么简单

    WebService介绍 首先我们来谈一下为什么需要学习webService这样的一个技术吧.... 问题一 如果我们的网站需要提供一个天气预报这样一个需求的话,那我们该怎么做????? 天气预报这么 ...

  6. 大数据(1):基于sogou.500w.utf8数据的MapReduce程序设计

    环境:centos7+hadoop2.5.2 1.使用ECLIPS具打包运行WORDCOUNT实例,统计莎士比亚文集各单词计数(文件SHAKESPEARE.TXT). ①WorldCount.java ...

  7. Python基本知识

    python是一门编程语言,这是我们学习python首先要了解的内容,那么编程语言又是什么,我们为什么需要编程语言 我们讲的话是汉语,英语或者其他语言,计算机也可以讲话,但是他只会说0,1,也只能看懂 ...

  8. 解决新版的vue-cli后无法通过ip访问vue项目

    今天遇到个问题,之前一直没遇到过,通过vue-cli脚手架搭建的项目,只能通过localhost访问,不能通过IP地址访问. 这是由 webpack 导致的,2017年11月12日的时候 webpac ...

  9. 记录Window系统下myeclipes连接linux下mysql所出现的一个bug

    记录myeclipes远程连接mysql所出现的一个bug 今天在玩框架hibernate时,出现一个非常费解的bug,话不多说,先看bug Access denied for user 'root' ...

  10. js中==和===区别

    简单来说: == 代表相同, ===代表严格相同, 为啥这么说呢, 这么理解: 当进行双等号比较时候: 先检查两个操作数数据类型,如果相同, 则进行===比较, 如果不同, 则愿意为你进行一次类型转换 ...