Hibernate级联操作和载入机制(二) cascade and fetch
2016-02-01 16:24
561 查看
上一篇介绍了Hibernate持久化对象时候的级联操作。本篇介绍读取时候的级联操作。
还是用上一篇的样例。一份问卷有多个问题。可是每一个问题仅仅能属于一份问卷。
我们先看測试用例:
读取出Id为1的问卷,用qn表示,打印出了问卷qn的名字,最后我希望打印出qn里包括了哪些问题,想打印出问题的题干内容。
可是运行:
报错了,懒载入异常,载入不到须要的实体。Questionnaire.question这个关联是没法关联上的。
那么出现这种错误的原因是:Hibernate在处理一对多关系的时候,默认是懒载入的。也就是说我从一这一方(Questionnaire)方。去读取多的一方(Question)方。是读取不到的。这么做能够减小数据库负担,可是万一我有这种需求,希望载入出多的这一方怎么做呢?代码例如以下:
在这里,配置上一个属性fetch,类型呢也是枚举,取值EAGER,这里还有另一个取值LAZY
ERGER:饥饿的,也就是立即就会载入出来
LAZY:懒的,不会载入关联对象
那么一对多默认的情况是懒载入,这里须要把fetch配置为EAGER,我们再看測试结果:
看SQL语句,使用了一个left outer join将关联对象查询出来。
没问题
另一个有意思的细节,代码例如以下:
这里我把饥饿载入去掉。让他恢复成默认的懒载入,測试用比例如以下:
for循环是循环输出问卷中的问题题干的,我把for循环放在事务提交之前,再运行:
看,也载入出来了,可是这么做。不是取的关联对象,细致看SQL语句发现,首先SQL语句分开运行了,也就是从问卷表查询了一次。再去依据问卷ID去问题表查了一次,这么做就不是级联了。
那么刚才为什么for循环放在外面也能够查出来了,原因是设置了饥饿载入后,问卷对象,连带下属的问题对象一起load到了内存中,所以把session关闭后也能够读取到。所以载入的机制和差别就在这里。
以下我们看从问题读取问卷
这边不设置fetch属性。我们看測试用例:
读取问题Id为1的问题。打印题干和所属问卷的名称:
没问题。说明,多对一的默认载入一这一方是EAGER的,所以不用多做设置,假设你不想载入出一这一方,那么做例如以下设置:
将载入机制设置为LAZY即可了。
总结一下:
在OneToMany的关系中(双向):
从1要载入n这一方。默认是LAZY的
从n要载入1这一方,默认是EAGER的
在级联和载入机制中:
cascade仅仅是负责增,删。改的级联
而读取须要级联载入的话,要用到fetch这个属性
还是用上一篇的样例。一份问卷有多个问题。可是每一个问题仅仅能属于一份问卷。
我们先看測试用例:
@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这个属性
相关文章推荐
- 再谈javascript原型继承
- SIP中的SDP offer/answer交换初探
- js获取服务端的头信息
- HTML和CSS复习知识点
- ztree树构造的一些解决方案,getNodes()的作用
- 【React Native开发】React Native控件之ViewPagerAndroid讲解以及美团首页顶部效果实例(17)
- jQuery速查表
- JavaScript 调用PHP
- Jquery简单动画的实现记录
- 基于单个 div 的 CSS 画图
- CSS—换行
- javascript 开发调试的利器(二) 获取当前执行函数及函数名称
- Jquery各个版本的区别
- css学习笔记20160130导航栏下拉菜单图像拼合媒体类型属性和值选择器
- angular中运用$cacheFactory服务时候报错
- 基于bootstrap pagination 的分页组件
- Notepad++代码自动完成(jQuery版)
- 为什么我们的web前端变的越来越复杂
- JS敏感信息泄露:不容忽视的WEB漏洞
- 前端编辑器之-Sublime Text3