Hibernate级联操作和加载机制(二) cascade and fetch

上一篇介绍了Hibernate持久化对象时候的级联操作,本篇介绍读取时候的级联操作。



还是用上一篇的例子,一份问卷有多个问题,但是每个问题只能属于一份问卷。我们先看测试用例:

     @Test
     public void testReadFromQuestionnaire(){
         Session session = sessionFactory.getCurrentSession();
         session.beginTransaction();

         Questionnaire qn = (Questionnaire) session.load(Questionnaire.class, 1);
         System.out.println(qn.getName());

         session.getTransaction().commit();

         for(Question q : qn.getQuestions()){
             System.out.println(q.getName());
         }
     }

读取出Id为1的问卷,用qn表示,打印出了问卷qn的名字,最后我希望打印出qn里包含了哪些问题,想打印出问题的题干内容。但是运行:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.ht.entity.one2manymany2one.bi.fetchcascade.Questionnaire.questions, no session or session was closed

报错了,懒加载异常,加载不到需要的实体,Questionnaire.question这个关联是没法关联上的。

那么出现这种错误的原因是:Hibernate在处理一对多关系的时候,默认是懒加载的,也就是说我从一这一方(Questionnaire)方,去读取多的一方(Question)方,是读取不到的,这么做可以减小数据库负担,但是万一我有这样的需求,希望加载出多的这一方怎么做呢?代码如下:

@OneToMany(mappedBy="questionnaire",cascade={CascadeType.ALL},fetch=FetchType.EAGER)
    public List<Question> getQuestions() {
        return questions;
    }

在这里,配置上一个属性fetch,类型呢也是枚举,取值EAGER,这里还有另一个取值LAZY

ERGER:饥饿的,也就是马上就会加载出来

LAZY:懒的,不会加载关联对象

那么一对多默认的情况是懒加载,这里需要把fetch配置为EAGER,我们再看测试结果:

Hibernate:
    select
        questionna0_.id as id6_1_,
        questionna0_.name as name6_1_,
        questionna0_.answers as answers6_1_,
        questions1_.questionnaireId as question3_3_,
        questions1_.id as id3_,
        questions1_.id as id7_0_,
        questions1_.name as name7_0_,
        questions1_.questionnaireId as question3_7_0_
    from
        t_questionnaire questionna0_
    left outer join
        t_question questions1_
            on questionna0_.id=questions1_.questionnaireId
    where
        questionna0_.id=?
XXX公司3月转正考试
1、Java是一门OOP语言吗?A、是  B、不是
2、Java是哪个公司的作品?A、SUN  B、MicroSoft

看SQL语句,使用了一个left outer join将关联对象查询出来。没问题



还有一个有意思的细节,代码如下:

    @OneToMany(mappedBy="questionnaire",cascade={CascadeType.ALL})
    public List<Question> getQuestions() {
        return questions;
    }

这里我把饥饿加载去掉,让他恢复成默认的懒加载,测试用例如下:

     @Test
     public void testReadFromQuestionnaire(){
         Session session = sessionFactory.getCurrentSession();
         session.beginTransaction();

         Questionnaire qn = (Questionnaire) session.load(Questionnaire.class, 1);
         System.out.println(qn.getName());

         for(Question q : qn.getQuestions()){
             System.out.println(q.getName());
         }

         session.getTransaction().commit();

     }

for循环是循环输出问卷中的问题题干的,我把for循环放在事务提交之前,再执行:

Hibernate:
    select
        questionna0_.id as id6_0_,
        questionna0_.name as name6_0_,
        questionna0_.answers as answers6_0_
    from
        t_questionnaire questionna0_
    where
        questionna0_.id=?
XXX公司3月转正考试
Hibernate:
    select
        questions0_.questionnaireId as question3_1_,
        questions0_.id as id1_,
        questions0_.id as id7_0_,
        questions0_.name as name7_0_,
        questions0_.questionnaireId as question3_7_0_
    from
        t_question questions0_
    where
        questions0_.questionnaireId=?
1、Java是一门OOP语言吗?A、是  B、不是
2、Java是哪个公司的作品?A、SUN  B、MicroSoft

看,也加载出来了,但是这么做,不是取的关联对象,仔细看SQL语句发现,首先SQL语句分开执行了,也就是从问卷表查询了一次,再去根据问卷ID去问题表查了一次,这么做就不是级联了。

那么刚才为什么for循环放在外面也可以查出来了,原因是设置了饥饿加载后,问卷对象,连带下属的问题对象一起load到了内存中,所以把session关闭后也可以读取到,所以加载的机制和区别就在这里。



下面我们看从问题读取问卷

    @ManyToOne(cascade={CascadeType.ALL})
    @JoinColumn(name="questionnaireId")
    public Questionnaire getQuestionnaire() {
        return questionnaire;
    }

这边不设置fetch属性,我们看测试用例:

     @Test
     public void testReadFromQuestion(){
         Session session = sessionFactory.getCurrentSession();
         session.beginTransaction();

         Question q = (Question) session.load(Question.class, 1);
         System.out.println(q.getName());

         session.getTransaction().commit();
         System.out.println(q.getName());
     }

读取问题Id为1的问题,打印题干和所属问卷的名称:

Hibernate:
    select
        question0_.id as id7_0_,
        question0_.name as name7_0_,
        question0_.questionnaireId as question3_7_0_
    from
        t_question question0_
    where
        question0_.id=?
Hibernate:
    select
        questionna0_.id as id6_1_,
        questionna0_.name as name6_1_,
        questionna0_.answers as answers6_1_,
        questions1_.questionnaireId as question3_3_,
        questions1_.id as id3_,
        questions1_.id as id7_0_,
        questions1_.name as name7_0_,
        questions1_.questionnaireId as question3_7_0_
    from
        t_questionnaire questionna0_
    left outer join
        t_question questions1_
            on questionna0_.id=questions1_.questionnaireId
    where
        questionna0_.id=?
1、Java是一门OOP语言吗?A、是  B、不是  XXX公司3月转正考试

没问题,说明,多对一的默认加载一这一方是EAGER的,所以不用多做设置,如果你不想加载出一这一方,那么做如下设置:

    @ManyToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
    @JoinColumn(name="questionnaireId")
    public Questionnaire getQuestionnaire() {
        return questionnaire;
    }

将加载机制设置为LAZY就行了。



总结一下:

在OneToMany的关系中(双向):

从1要加载n这一方,默认是LAZY的

从n要加载1这一方,默认是EAGER的

在级联和加载机制中:

cascade只是负责增,删,改的级联

而读取需要级联加载的话,要用到fetch这个属性

时间: 2024-10-06 21:13:56

Hibernate级联操作和加载机制(二) cascade and fetch的相关文章

Hibernate级联操作解密(inverse和cascade)

总结: Cascade:对级联操作进行限制,有如下几个参数: all : 所有情况下均进行关联操作.  none:所有情况下均不进行关联操作.这是默认值.  save-update:在执行save/update/saveOrUpdate时进行关联操作.  delete:在执行delete时进行关联操作. Inverse:在一对多模型中,只能在一的一方设置,inverse的作用就是在级联发生后,会再次更新子表数据的外键为主表的主键.确保子表外键不会为空. 下面演示一个班级学生的例子(一对多): 班

hibernate级联操作

在关系中加入如下代码分别表示不同的级联操作 例如: <many-to-one name="inv" column="igid" class="entity.inventory" cascade="delete"></many-to-one> 1.级联保存 cascade=”save-update” 2.级联删除 cascade=”delete” 3.所有级联 cascade=”all 4.不使用级联 c

【SSH三大框架】Hibernate基础第九篇:cascade关联关系的级联操作

这里要说的是Hibernate的关联关系的级联操作,使用cascade属性控制. 依旧用部门和员工举例.多个员工相应一个部门(多对一关联关系) 员工类:Employee.java package cn.itcast.hibernate.domain; public class Employee { private int id; private String name; private Department depart; public int getId() { return id; } pu

Hibernate的Cascade——级联操作

在Hibernate中,针对持久化实体的配置文件中有Cascade这样一个属性,顾名思义就是级联,也就是说在操作当 前实体时,针对当前实体的操作会影响到相应配置的关联实体.比如针对当前实体进行保存操作时,会同时保存 与其关联的实体,当然这种额外操作的产生取决于你是否在当前实体的配置文件中对关联实体的元素配置了 Cascade属性. 而需要进行级联的操作可能涉及到增加,修改,删除等相应的数据库操作,具体Cascade的取值则 分为多种,如all,表示针对所有操作都会进行级联,如save-updat

hibernate中关系操作(inverse)和级联操作(cascade)详解

以用户.角色.用户文件为例讲解inverse(关系操作)和(cascade)操作 inverse 取值 true(不维护关系)或false(维护关系  默认为false) 该属性主要操作的是外键 cascade 取值null(默认值).save-update .all .delete 用户.角色是多对多的关系 用户的映射文件表示: <hibernate-mapping> <class name="com.xing.elec.domain.ElecUser" table

Hibernate 配置文件属性之 级联操作cascade 性能优化inverse

<?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 pack

Hibernate关联操作、查询操作、高级特性、并发处理机制

本文所需的数据库初始文件,Hibernate常用操作的完整示例代码(包含所有Hibernate操作所需jar文件)提供下载学习:http://download.csdn.net/detail/daijin888888/9551724 1.Hibernate关联映射 1)什么是关联映射? 如果表之间具有关联关系,Hibernate允许我们在hbm.xml中描述他们的关联关系,然后在我们操作其中一张表时,自动的根据这种关系操作到另外的关系表,那么这种关联关系的设置,我们称之为关联映射. 2)关联映射

Hibernate(八)__级联操作、struts+hibernate+接口编程架构

级联操作 所谓级联操作就是说,当你进行主对象某个操作时,从对象hibernate自动完成相应操作. 比如: Department <---->Student 对象关系,我希望当我删除一个department ,那么就自动删除该部门的所有学生. 再比如: bbs项目主帖<---->回帖 , 把主帖删除,那我们就希望把该主帖的回帖自动删除,这样我们可以使用级联(cascade)操作. 案例:如何配置级联操作,当删除某个部门的时候,我们自动删除其学生. 首先我们在配置文件中修改: <

【Hibernate学习】——级联操作

级联策略:负责控制关联两端对象到对象的级联关系的操作,包括更新.删除等,也就是说对一个对象进行更新.删除时,其它对象也受影响,比如我删除一个对象,那么跟它是多对一关系的对象也全部被删除. 在前面用了抓取策略之后,后面试了一下级联操作的注解:cascadeType.MERGE方式 @OneToOne(cascade={CascadeType.MERGE}) @JoinColumn(name="company_id") publicCompany getCompany() { return