[转]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的代理模式 区别和联系 静态代理和动态代理 概念 代 ...
随机推荐
- 迈出物联网的第一步,玩儿一下Arduino
大家知道,现在物联网Internet of Things(IoT) 方兴未艾,各种智能设备层出不穷,手表.手环.甚至运动鞋等可穿戴设备,还有智能家居产品,无时无刻不冲击着我们的思想和眼球.Autode ...
- 初识JAVA之OOP
有一段时间没发博客了,每次手打还是很累,但感觉很充实.. 最近发现很多初学者到了面向对象编程这个知识点时,不太清楚类是如何转化成为对象的,很是困扰,今天我在这里谈谈我的理解,大家一起来研究学习... ...
- Android-正方形的容器
package liu.myrecyleviewchoosephoto.view; import android.content.Context; import android.util.Attrib ...
- java中文件的I/O操作
java中文件的读写操作 (一) (1)java中文件的字节转成字符读操作 FileInputStream fStream = new FileInputStream("test.txt&q ...
- Java异常之自定义异常
哎呀,妈呀,又出异常了!俗话说:"代码虐我千百遍,我待代码如初恋". 小Alan最近一直在忙着工作,已经很久没有写写东西来加深自己的理解了,今天来跟大家聊聊Java异常.Java异 ...
- 十几张表的join(千万级/百万级表) 7hours-->5mins
================START============================== 来了一个mail说是job跑得很慢,调查下原因 先来看下sql: SELECT h.order_ ...
- 【Linux】解决Wesnoth中文乱码问题
现在使用的系统是Linux Mint 18,安装了Wesnoth,发现打开之后部分中文显示正常,部分中文显示为乱码. 谷歌出的很多办法都只给出了几条指令,但并不具有普适性,我提供一种新的方法,通过修改 ...
- percona教程:MySQL GROUP_CONCAT的使用
percona有一篇blog: The power of MySQL GROUP_CONCAT 比较详细地介绍了GROUP_CONCAT函数的用法.简单地翻译了一下. 假设你有4名工程师,这周他们为6 ...
- docker-9 supervisord 参考docker从入门到实战
参考docker从入门到实战 使用 Supervisor 来管理进程 Docker 容器在启动的时候开启单个进程,比如,一个 ssh 或者 apache 的 daemon 服务.但我们经常需要在一个机 ...
- java http工具类和HttpUrlConnection上传文件分析
利用java中的HttpUrlConnection上传文件,我们其实只要知道Http协议上传文件的标准格式.那么就可以用任何一门语言来模拟浏览器上传文件.下面有几篇文章从http协议入手介绍了java ...