[转]Hibernate延迟加载与opensessioninviewFilter
原文地址:http://blog.csdn.net/a19881029/article/details/7916702
hibernate延迟加载:
一个person对应多个school,使用hibernate处理关联关系:
T_PERSON表:
id | name | age |
1 | person1 | 11 |
T_SCHOOL表:
id | schoolName | personId |
1 | school1 | 1 |
2 | school2 | 1 |
3 | school3 | 1 |
person类:
- public class Person {
- public Person(){}
- private int id;
- private String name;
- private int age;
- private Set<School> schools = new HashSet<School>();
- 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;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public Set<School> getSchools() {
- return schools;
- }
- public void setSchools(Set<School> schools) {
- this.schools = schools;
- }
- }
school类:
- public class School {
- public School(){}
- private int id;
- private String schoolName;
- private int personId;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getSchoolName() {
- return schoolName;
- }
- public void setSchoolName(String schoolName) {
- this.schoolName = schoolName;
- }
- public int getPersonId() {
- return personId;
- }
- public void setPersonId(int personId) {
- this.personId = personId;
- }
- }
person类的映射文件为:
- <hibernate-mapping>
- <class name="com.po.Person" table="T_PERSON">
- <id name="id" column="id" type="java.lang.Integer">
- <generator class="assigned"/>
- </id>
- <property name="name" column="name" type="java.lang.String" length="20"/>
- <property name="age" column="age" type="java.lang.Integer"/>
- <set name="schools" table="T_SCHOOL" inverse="true" lazy="false">
- <key column="personId"/>
- <one-to-many class="com.po.School"/>
- </set>
- </class>
- </hibernate-mapping>
school类的映射文件为:
- <hibernate-mapping>
- <class name="com.po.School" table="T_SCHOOL">
- <id name="id" column="id" type="java.lang.Integer">
- <generator class="assigned"/>
- </id>
- <property name="schoolName" column="schoolName" type="java.lang.String"
- length="20"/>
- <property name="personId" column="personId" type="java.lang.Integer"/>
- </class>
- </hibernate-mapping>
由person类的配置文件可知没有使用延迟加载(person类的映射文件中schools集合属性lazy=“false”)
- public static void main(String[] args) {
- Configuration conf = new Configuration();
- SessionFactory sessionFactory = conf.configure().buildSessionFactory();
- Session session = sessionFactory.openSession();
- String hql = "from Person";
- Query q = session.createQuery(hql);
- List<Person> result = q.list();
- session.flush();
- session.close();
- System.out.println("end");
- }
观察了一下打印信息,有2条sql:
- Hibernate: select person0_.id as id0_,
- person0_.name as name0_, person0_.age as age0_ from T_PERSON person0_
- Hibernate: select schools0_.personId as personId0_1_, schools0_.id as id1_,
- schools0_.id as id1_0_, schools0_.schoolName as schoolName1_0_,
- schools0_.personId as personId1_0_ from T_SCHOOL schools0_
- where schools0_.personId=?
- end
在查询出person的同时查询出和person相关联的school,当数据量比较大的时候这是相当消耗性能的
修改一下使用延迟加载(person类的映射文件中schools集合属性lazy=“true”)
还是使用上面的main方法,打印信息中只有1条sql:
- Hibernate: select person0_.id as id0_, person0_.name as name0_,
- person0_.age as age0_ from T_PERSON person0_
- end
修改一下mian方法:
- public static void main(String[] args) {
- Configuration conf = new Configuration();
- SessionFactory sessionFactory = conf.configure().buildSessionFactory();
- Session session = sessionFactory.openSession();
- String hql = "from Person";
- Query q = session.createQuery(hql);
- List<Person> result = q.list();
- System.out.println("person");
- Set<School> s = result.get(0).getSchools();
- System.out.println("schools");
- session.flush();
- session.close();
- System.out.println("end");
- }
注意这里面并没有真正使用person中的Set<school>,创建了一个Set<school>的引用,再次观察打印信息:
- Hibernate: select person0_.id as id0_, person0_.name as name0_,
- person0_.age as age0_ from T_PERSON person0_
- person
- schools
- end
还是一条,再次修改一下main方法:
- public static void main(String[] args) {
- Configuration conf = new Configuration();
- SessionFactory sessionFactory = conf.configure().buildSessionFactory();
- Session session = sessionFactory.openSession();
- String hql = "from Person";
- Query q = session.createQuery(hql);
- List<Person> result = q.list();
- System.out.println("person");
- Set<School> s = result.get(0).getSchools();
- s.iterator();
- System.out.println("schools");
- session.flush();
- session.close();
- System.out.println("end");
- }
这次的打印信息中有2条sql了:
- Hibernate: select person0_.id as id0_, person0_.name as name0_,
- person0_.age as age0_ from T_PERSON person0_
- person
- Hibernate: select schools0_.personId as personId0_1_,
- schools0_.id as id1_, schools0_.id as id1_0_, schools0_.schoolName
- as schoolName1_0_, schools0_.personId as personId1_0_
- from T_SCHOOL schools0_ where schools0_.personId=?
- schools
- end
说明只有当程序中真正使用设置为延迟加载的对象时,hibernate才会去加载该对象。
但是应该注意到在上面的代码中,即使是调用设置为延迟加载的s对象,也是在同一个session中调用,但是实际项目中,肯定不会在同一个
session中使用s对象,而是经常在dao层取出person,而在bo层甚至是view层中延迟加载s进行业务逻辑处理或是进行页面展示,这个时候
session已经关闭了。
- public static void main(String[] args) {
- Configuration conf = new Configuration();
- SessionFactory sessionFactory = conf.configure().buildSessionFactory();
- Session session = sessionFactory.openSession();
- String hql = "from Person";
- Query q = session.createQuery(hql);
- List<Person> result = q.list();
- session.flush();
- session.close();
- System.out.println("end");
- Set<School> s = result.get(0).getSchools();
- s.iterator();
- System.out.println("schools");
- }
如果不在同一个session中使用延迟加载对象,则会报如下的错误:
- Hibernate: select person0_.id as id0_, person0_.name as name0_, person0_.age as age0_ from T_PERSON person0_
- end
- Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.po.Person.schools, no session or session was closed
- at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
- at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
- at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
- at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)
- at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:186)
- at com.test.test.main(test.java:27)
会提示找不到session或session已关闭,这时候如何处理呢,就是下面opensessioninviewFilter
opensessioninviewFilter:
官方文档对他的描述:
- Servlet 2.3 Filter that binds a Hibernate Session to the thread for
- the entire processing of the request. Intended for the "Open Session in View" pattern,
- i.e. to allow for lazy loading in web views despite the original transactions
- already being completed.
- This filter makes Hibernate Sessions available via the current thread,
- which will be autodetected by transaction managers.
- It is suitable for service layer transactions via HibernateTransactionManager
- or JtaTransactionManager as well as for non-transactional execution
- (if configured appropriately).
- NOTE: This filter will by default not flush the Hibernate Session,
- with the flush mode set to FlushMode.NEVER. It assumes to be used in combination
- with service layer transactions that care for the flushing:
- The active transaction manager will temporarily change the flush mode
- to FlushMode.AUTO during a read-write transaction,
- with the flush mode reset to FlushMode.NEVER at the end of each transaction.
- If you intend to use this filter without transactions,
- consider changing the default flush mode (through the "flushMode" property).
- WARNING: Applying this filter to existing logic can cause issues
- that have not appeared before, through the use of a single Hibernate Session
- for the processing of an entire request. In particular, the reassociation of persistent
- objects with a Hibernate Session has to occur at the very beginning of request processing,
- to avoid clashes with already loaded instances of the same objects.
- Alternatively, turn this filter into deferred close mode,
- by specifying "singleSession"="false":
- It will not use a single session per request then,
- but rather let each data access operation or transaction use its own session
- (like without Open Session in View).
- Each of those sessions will be registered for deferred close,
- though, actually processed at request completion.
- A single session per request allows for most efficient first-level caching,
- but can cause side effects, for example on saveOrUpdate or when continuing
- after a rolled-back transaction.
- The deferred close strategy is as safe as no Open Session in View in that respect,
- while still allowing for lazy loading in views
- (but not providing a first-level cache for the entire request).
- Looks up the SessionFactory in Spring's root web application context.
- Supports a "sessionFactoryBeanName" filter init-param in web.xml;
- the default bean name is "sessionFactory".
- Looks up the SessionFactory on each request, to avoid initialization order issues
- (when using ContextLoaderServlet,
- the root application context will get initialized after this filter).
该filter会将session绑定至当前请求的线程上,这样只要是在当前请求的生命周期内,可以随时访问session,只需要在项目的web.xml文件中增加如下配置即可:
- <filter>
- <filter-name>hibernateOpenSessionInViewFilter</filter-name>
- <filter-class>org.springside.modules.orm.hibernate.OpenSessionInViewFilter</filter-class>
- <init-param>
- <param-name>flushMode</param-name>
- <param-value>AUTO</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>hibernateOpenSessionInViewFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
- </filter> -->
只要增加该过滤器即可,但是要特别注意的是:该过滤器需要配置在struts2的过滤器之前。
还有一点要注意的是(参见文档说明中的note):在使用
opensessioninviewFilter时,session的flushMode默认是never的,如果想要进行保存,更新等操作,必须要修改
session的flushMode,有2种方式进行修改(当然可以重写filter的方法,有点麻烦不讨论了):
1,像上面一样,当在web.xml中注册filter时,增加初始化参数(init-param,灵活性不高)
2,使用声明式事务代理(灵活性很高,推荐)
需要注意的是(参见文档说明中的warning):由于session会
和当前request绑定,所以在已经存在的逻辑上添加该filter会产生以前没有产生过的问题。每次通过session取得hibernate持久化
对象时都要重新关联,避免获得保存在session中以前加载过的相同对象。
举例如下:
数据库中的表的主键都统一通过ColumnSequence这张表来管理,ColumnSequence表通过tablename字段和columnname字段来唯一的确定一张表的主键,并通过sequencenum字段对表的主键进行管理:
tablename | columnname | sequencenum |
表名 | 列名 | 主键值 |
当需要向数据库中的某张表插入新的数据时,首先更新在ColumnSequence表中管理的对应表的主键值为当前值加1,再将更新后的值取出作为即将插入数据的主键值:
- public Integer getSequence(String tableName, String columnName) throws DAOException {
- try {
- ColumnSequencePK id = new ColumnSequencePK(tableName,columnName);
- String hql = "update ColumnSequence ";
- hql += " set sequencenum = sequencenum + 1";
- hql += " where tablename = '"+tableName+"' and columnname = '"+columnName+"'";
- this.update(hql);
- /* //在查询前清除掉session中的缓存,以免接下来的get方法从缓存中读取数据而不是从数据库中重新查询数据
- this.getMyHibernateTemplate().clear(); */
- ColumnSequence tmp = (ColumnSequence) this.getByID(ColumnSequence.class,id);
- return tmp.getSequencenum();
- }catch (Exception e) {
- throw new DAOException(e, this.getClass());
- }
- }
当在同一次请求中连续2次保存同类对象,则需连续2次通过上面的方法获取主键值。
第一次通过ColumnSequence tmp = (ColumnSequence)
this.getByID(ColumnSequence.class,id)获取ColumnSequence对象时,hibernate会将该对象保
存在session中,当第二次通过ColumnSequence tmp = (ColumnSequence)
this.getByID(ColumnSequence.class,id)获取ColumnSequence对象时,hibernate发现
session中存有相同的对象(getByID的参数ColumnSequence.class以及id都相同),就不会进行sql查询,而是直接返回
session中已存在的ColumnSequence对象,并将该对象的sequencenum返回作为主键值,要注意的是第二次获取
ColumnSequence对象之前是进行了一次更新操作的,数据库中的sequencenum被更新了2次,但是2个需要保存到数据库中的对象获取到
的主键值却是同一个(2次getByID方法返回的都是同一个ColumnSequence对象),这时候当事务提交时会报错,提示主键已存在,只有第一
个需要保存的对象成功的存入了数据库中。
解决方式:
上面代码中的注释部分,在第二次获取ColumnSequence之前清理掉session中的缓存,hibernate在session中找不到
相同的对象,只能去数据库中查询获得对象,这时候第二次取到的ColumnSequence对象就变成第二次更新后的值了,而不是第一次取到的
ColumnSequence对象在session中的缓存,当然主键也就是更新后的了。
这种情况比较少见,但是应当注意
warning中还给出了一种解决方式,即在web.xml中的opensessioninviewFilter初始化参数中添加如下项:
- <init-param>
- <param-name>singleSession</param-name>
- <param-value>false</param-value>
- </init-param>
但是这种配置方式有2个问题:
1,相当于没有使用opensessioninviewFilter
2,明显的增多hibernate打开的session数量,对hibernate性能影响严重
[转]Hibernate延迟加载与opensessioninviewFilter的更多相关文章
- Hibernate延迟加载与opensessioninviewFilter
转自:https://blog.csdn.net/skk_7/article/details/17917339 hibernate延迟加载: 一个person对应多个school,使用hibernat ...
- SSH项目web.xml文件的常用配置【struts2的过滤器、spring监听器、解决Hibernate延迟加载问题的过滤器、解决中文乱码的过滤器】
配置web.xml(struts2的过滤器.spring监听器.解决Hibernate延迟加载问题的过滤器.解决中文乱码的过滤器) <!-- 解决中文乱码问题 --> <filter ...
- Hibernate延迟加载Lazy
Hibernate延迟加载Lazy 延迟加载(lazy load)又称为懒加载,延迟加载的机制是为了避免一些无谓性能的开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作 如 ...
- Hibernate学习--hibernate延迟加载原理(动态代理)
在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...
- 【转】hibernate 延迟加载
Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...
- hibernate 延迟加载深入分析(persistentSet的延迟加载)
Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.Hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...
- Hibernate 延迟加载 分析
出处:http://www.ibm.com/developerworks/cn/java/j-lo-hibernatelazy/#icomments Hibernate 的延迟加载(lazy load ...
- Hibernate学习--hibernate延迟加载原理-动态代理(阿里电面)
在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...
- Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式
Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...
随机推荐
- iOS之两个ImageView实现图片滚动
原创作者:codingZero 导语 在不少项目中,都会有图片轮播这个功能,现在网上关于图片轮播的框架层出不穷,千奇百怪,笔者根据自己的思路,用两个imageView也实现了图片轮播,这里说说笔者的主 ...
- Android自定义ViewGroup,实现自动换行
学习<Android开发艺术探索>中自定义ViewGroup章节 自定义ViewGroup总结的知识点 一.自定义ViewGroup中,onMeasure理解 onMeasure(int ...
- UIweib的简单实用
- 关于Hibernate 的数据库配置
<hibernate-configuration> <session-factory name="mySessionFactory"> ...
- 作业配置规范文档[MS SQL]
作业配置规范文档(MS SQL) 文档类型 MS SQL数据库作业配置规范文档 创建日期 2015-07-30 版本变化 V3.0 修改记录 修改人 修改日期 版本 修改描述 潇湘隐者 2015-08 ...
- C#:结构
1. 简单示例 // 定义结构 public struct Person { public string name; public int age; } class Program { static ...
- 解决MyEclipse中的js报错的小方法
今天,下了个模版,但是导进去的时候发现js会报错.看了下其他都没有错误.而有一个js报错误,请原谅我有点红色强迫症,不能留一点红色 . 错误如下:Syntax error on token " ...
- explicit抑制隐型转换
本文出自 http://www.cnblogs.com/cutepig/ 按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示: clas ...
- Java读写文本文件操作
package com.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; ...
- 烂泥:openvpn tun模式下客户端与内网机器通信
本文由秀依林枫提供友情赞助,首发于烂泥行天下 前两篇文章我们介绍了有关openvpn的搭建与配置文件的讲解,这篇文章我们再聊介绍下,在tun模式下openvpn客户端如何与内网机器通信的问题. 一.实 ...