原文地址: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类:

  1. public class Person {
  2. public Person(){}
  3. private int id;
  4. private String name;
  5. private int age;
  6. private Set<School> schools = new HashSet<School>();
  7. public int getId() {
  8. return id;
  9. }
  10. public void setId(int id) {
  11. this.id = id;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public int getAge() {
  20. return age;
  21. }
  22. public void setAge(int age) {
  23. this.age = age;
  24. }
  25. public Set<School> getSchools() {
  26. return schools;
  27. }
  28. public void setSchools(Set<School> schools) {
  29. this.schools = schools;
  30. }
  31. }

school类:

  1. public class School {
  2. public School(){}
  3. private int id;
  4. private String schoolName;
  5. private int personId;
  6. public int getId() {
  7. return id;
  8. }
  9. public void setId(int id) {
  10. this.id = id;
  11. }
  12. public String getSchoolName() {
  13. return schoolName;
  14. }
  15. public void setSchoolName(String schoolName) {
  16. this.schoolName = schoolName;
  17. }
  18. public int getPersonId() {
  19. return personId;
  20. }
  21. public void setPersonId(int personId) {
  22. this.personId = personId;
  23. }
  24. }

person类的映射文件为:

  1. <hibernate-mapping>
  2. <class name="com.po.Person" table="T_PERSON">
  3. <id name="id" column="id" type="java.lang.Integer">
  4. <generator class="assigned"/>
  5. </id>
  6. <property name="name" column="name" type="java.lang.String" length="20"/>
  7. <property name="age" column="age" type="java.lang.Integer"/>
  8. <set name="schools" table="T_SCHOOL" inverse="true" lazy="false">
  9. <key column="personId"/>
  10. <one-to-many class="com.po.School"/>
  11. </set>
  12. </class>
  13. </hibernate-mapping>

school类的映射文件为:

  1. <hibernate-mapping>
  2. <class name="com.po.School" table="T_SCHOOL">
  3. <id name="id" column="id" type="java.lang.Integer">
  4. <generator class="assigned"/>
  5. </id>
  6. <property name="schoolName" column="schoolName" type="java.lang.String"
  7. length="20"/>
  8. <property name="personId" column="personId" type="java.lang.Integer"/>
  9. </class>
  10. </hibernate-mapping>

由person类的配置文件可知没有使用延迟加载(person类的映射文件中schools集合属性lazy=“false”)

  1. public static void main(String[] args) {
  2. Configuration conf = new Configuration();
  3. SessionFactory sessionFactory = conf.configure().buildSessionFactory();
  4. Session session = sessionFactory.openSession();
  5. String hql = "from Person";
  6. Query q =  session.createQuery(hql);
  7. List<Person> result = q.list();
  8. session.flush();
  9. session.close();
  10. System.out.println("end");
  11. }

观察了一下打印信息,有2条sql:

  1. Hibernate: select person0_.id as id0_,
  2. person0_.name as name0_, person0_.age as age0_ from T_PERSON person0_
  3. Hibernate: select schools0_.personId as personId0_1_, schools0_.id as id1_,
  4. schools0_.id as id1_0_, schools0_.schoolName as schoolName1_0_,
  5. schools0_.personId as personId1_0_ from T_SCHOOL schools0_
  6. where schools0_.personId=?
  7. end

在查询出person的同时查询出和person相关联的school,当数据量比较大的时候这是相当消耗性能的
修改一下使用延迟加载(person类的映射文件中schools集合属性lazy=“true”)
还是使用上面的main方法,打印信息中只有1条sql:

  1. Hibernate: select person0_.id as id0_, person0_.name as name0_,
  2. person0_.age as age0_ from T_PERSON person0_
  3. end

修改一下mian方法:

  1. public static void main(String[] args) {
  2. Configuration conf = new Configuration();
  3. SessionFactory sessionFactory = conf.configure().buildSessionFactory();
  4. Session session = sessionFactory.openSession();
  5. String hql = "from Person";
  6. Query q =  session.createQuery(hql);
  7. List<Person> result = q.list();
  8. System.out.println("person");
  9. Set<School> s = result.get(0).getSchools();
  10. System.out.println("schools");
  11. session.flush();
  12. session.close();
  13. System.out.println("end");
  14. }

注意这里面并没有真正使用person中的Set<school>,创建了一个Set<school>的引用,再次观察打印信息:

  1. Hibernate: select person0_.id as id0_, person0_.name as name0_,
  2. person0_.age as age0_ from T_PERSON person0_
  3. person
  4. schools
  5. end

还是一条,再次修改一下main方法:

  1. public static void main(String[] args) {
  2. Configuration conf = new Configuration();
  3. SessionFactory sessionFactory = conf.configure().buildSessionFactory();
  4. Session session = sessionFactory.openSession();
  5. String hql = "from Person";
  6. Query q =  session.createQuery(hql);
  7. List<Person> result = q.list();
  8. System.out.println("person");
  9. Set<School> s = result.get(0).getSchools();
  10. s.iterator();
  11. System.out.println("schools");
  12. session.flush();
  13. session.close();
  14. System.out.println("end");
  15. }

这次的打印信息中有2条sql了:

  1. Hibernate: select person0_.id as id0_, person0_.name as name0_,
  2. person0_.age as age0_ from T_PERSON person0_
  3. person
  4. Hibernate: select schools0_.personId as personId0_1_,
  5. schools0_.id as id1_, schools0_.id as id1_0_, schools0_.schoolName
  6. as schoolName1_0_, schools0_.personId as personId1_0_
  7. from T_SCHOOL schools0_ where schools0_.personId=?
  8. schools
  9. end

说明只有当程序中真正使用设置为延迟加载的对象时,hibernate才会去加载该对象。

但是应该注意到在上面的代码中,即使是调用设置为延迟加载的s对象,也是在同一个session中调用,但是实际项目中,肯定不会在同一个
session中使用s对象,而是经常在dao层取出person,而在bo层甚至是view层中延迟加载s进行业务逻辑处理或是进行页面展示,这个时候
session已经关闭了。

  1. public static void main(String[] args) {
  2. Configuration conf = new Configuration();
  3. SessionFactory sessionFactory = conf.configure().buildSessionFactory();
  4. Session session = sessionFactory.openSession();
  5. String hql = "from Person";
  6. Query q =  session.createQuery(hql);
  7. List<Person> result = q.list();
  8. session.flush();
  9. session.close();
  10. System.out.println("end");
  11. Set<School> s = result.get(0).getSchools();
  12. s.iterator();
  13. System.out.println("schools");
  14. }

如果不在同一个session中使用延迟加载对象,则会报如下的错误:

  1. Hibernate: select person0_.id as id0_, person0_.name as name0_, person0_.age as age0_ from T_PERSON person0_
  2. end
  3. 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
  4. at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
  5. at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
  6. at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
  7. at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)
  8. at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:186)
  9. at com.test.test.main(test.java:27)

会提示找不到session或session已关闭,这时候如何处理呢,就是下面opensessioninviewFilter

opensessioninviewFilter:

官方文档对他的描述:

  1. Servlet 2.3 Filter that binds a Hibernate Session to the thread for
  2. the entire processing of the request. Intended for the "Open Session in View" pattern,
  3. i.e. to allow for lazy loading in web views despite the original transactions
  4. already being completed.
  5. This filter makes Hibernate Sessions available via the current thread,
  6. which will be autodetected by transaction managers.
  7. It is suitable for service layer transactions via HibernateTransactionManager
  8. or JtaTransactionManager as well as for non-transactional execution
  9. (if configured appropriately).
  10. NOTE: This filter will by default not flush the Hibernate Session,
  11. with the flush mode set to FlushMode.NEVER. It assumes to be used in combination
  12. with service layer transactions that care for the flushing:
  13. The active transaction manager will temporarily change the flush mode
  14. to FlushMode.AUTO during a read-write transaction,
  15. with the flush mode reset to FlushMode.NEVER at the end of each transaction.
  16. If you intend to use this filter without transactions,
  17. consider changing the default flush mode (through the "flushMode" property).
  18. WARNING: Applying this filter to existing logic can cause issues
  19. that have not appeared before, through the use of a single Hibernate Session
  20. for the processing of an entire request. In particular, the reassociation of persistent
  21. objects with a Hibernate Session has to occur at the very beginning of request processing,
  22. to avoid clashes with already loaded instances of the same objects.
  23. Alternatively, turn this filter into deferred close mode,
  24. by specifying "singleSession"="false":
  25. It will not use a single session per request then,
  26. but rather let each data access operation or transaction use its own session
  27. (like without Open Session in View).
  28. Each of those sessions will be registered for deferred close,
  29. though, actually processed at request completion.
  30. A single session per request allows for most efficient first-level caching,
  31. but can cause side effects, for example on saveOrUpdate or when continuing
  32. after a rolled-back transaction.
  33. The deferred close strategy is as safe as no Open Session in View in that respect,
  34. while still allowing for lazy loading in views
  35. (but not providing a first-level cache for the entire request).
  36. Looks up the SessionFactory in Spring's root web application context.
  37. Supports a "sessionFactoryBeanName" filter init-param in web.xml;
  38. the default bean name is "sessionFactory".
  39. Looks up the SessionFactory on each request, to avoid initialization order issues
  40. (when using ContextLoaderServlet,
  41. the root application context will get initialized after this filter).

该filter会将session绑定至当前请求的线程上,这样只要是在当前请求的生命周期内,可以随时访问session,只需要在项目的web.xml文件中增加如下配置即可:

  1. <filter>
  2. <filter-name>hibernateOpenSessionInViewFilter</filter-name>
  3. <filter-class>org.springside.modules.orm.hibernate.OpenSessionInViewFilter</filter-class>
  4. <init-param>
  5. <param-name>flushMode</param-name>
  6. <param-value>AUTO</param-value>
  7. </init-param>
  8. </filter>
  9. <filter-mapping>
  10. <filter-name>hibernateOpenSessionInViewFilter</filter-name>
  11. <url-pattern>/*</url-pattern>
  12. </filter-mapping>
  13. <!--  <filter>
  14. <filter-name>struts2</filter-name>
  15. <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  16. </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,再将更新后的值取出作为即将插入数据的主键值:

  1. public Integer getSequence(String tableName, String columnName) throws DAOException {
  2. try {
  3. ColumnSequencePK id = new ColumnSequencePK(tableName,columnName);
  4. String hql = "update ColumnSequence ";
  5. hql += " set sequencenum = sequencenum + 1";
  6. hql += " where tablename = '"+tableName+"' and columnname = '"+columnName+"'";
  7. this.update(hql);
  8. /*  //在查询前清除掉session中的缓存,以免接下来的get方法从缓存中读取数据而不是从数据库中重新查询数据
  9. this.getMyHibernateTemplate().clear();                      */
  10. ColumnSequence tmp = (ColumnSequence) this.getByID(ColumnSequence.class,id);
  11. return tmp.getSequencenum();
  12. }catch (Exception e) {
  13. throw new DAOException(e, this.getClass());
  14. }
  15. }

当在同一次请求中连续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初始化参数中添加如下项:

  1. <init-param>
  2. <param-name>singleSession</param-name>
  3. <param-value>false</param-value>
  4. </init-param>

但是这种配置方式有2个问题:

1,相当于没有使用opensessioninviewFilter

2,明显的增多hibernate打开的session数量,对hibernate性能影响严重

[转]Hibernate延迟加载与opensessioninviewFilter的更多相关文章

  1. Hibernate延迟加载与opensessioninviewFilter

    转自:https://blog.csdn.net/skk_7/article/details/17917339 hibernate延迟加载: 一个person对应多个school,使用hibernat ...

  2. SSH项目web.xml文件的常用配置【struts2的过滤器、spring监听器、解决Hibernate延迟加载问题的过滤器、解决中文乱码的过滤器】

    配置web.xml(struts2的过滤器.spring监听器.解决Hibernate延迟加载问题的过滤器.解决中文乱码的过滤器) <!-- 解决中文乱码问题 --> <filter ...

  3. Hibernate延迟加载Lazy

    Hibernate延迟加载Lazy 延迟加载(lazy load)又称为懒加载,延迟加载的机制是为了避免一些无谓性能的开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作 如 ...

  4. Hibernate学习--hibernate延迟加载原理(动态代理)

    在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...

  5. 【转】hibernate 延迟加载

    Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...

  6. hibernate 延迟加载深入分析(persistentSet的延迟加载)

    Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.Hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...

  7. Hibernate 延迟加载 分析

    出处:http://www.ibm.com/developerworks/cn/java/j-lo-hibernatelazy/#icomments Hibernate 的延迟加载(lazy load ...

  8. Hibernate学习--hibernate延迟加载原理-动态代理(阿里电面)

    在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...

  9. Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式

    Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...

随机推荐

  1. 如何保证Service在后台不被kill

    如何保证Service在后台不被kill 相信很多Android开发者在面试过程中会经常被问到“如何保证Service在后台不被kill”这个问题,总结了下一些大神给的答案. 引用知乎Android ...

  2. HTML5-03 页面布局

    概述 HTML 文档中的元素是一个接着一个排列的,只是简单地在在块级元素的前后加上拆行,是一种流水布局.但是,我们所见到的 Web 页面按照一定的规则布局排版的(通常是多列的),所以就要借助一定的方法 ...

  3. Petya勒索木马

    同事小学妹神好奇心,在陌生群里下载了个软件,接下来就是自动重启无法开机. 找我一看,凭我专业帮妹纸装系统多年的经验,起初也不觉得有啥困难,兼容模式下重启,接下来出现这个: 按下any key后: 试了 ...

  4. vim vundle 安装Base16 Vim主题

    1.vim /etc/vimrc set background=dark colorscheme base16-default 2.同样在vimrc中的vundle位置添加 Plugin 'chris ...

  5. sql 判断 函数 存储过程是否存在的方法

    下面为您介绍sql下用了判断各种资源是否存在的代码,需要的朋友可以参考下,希望对您学习sql的函数及数据库能够有所帮助.库是否存在if exists(select * from master..sys ...

  6. 为什么忘记commit也会造成select查询的性能问题

    今天遇到一个很有意思的问题,一个开发人员反馈在测试服务器ORACLE数据库执行的一条简单SQL语句非常缓慢,他写的一个SQL没有返回任何数据,但是耗费了几分钟的时间.让我检查分析一下原因,分析解决过后 ...

  7. Memcached学习笔记

    [TOC] 前言 此为学习笔记汇总,如有纰漏之处,还望不吝指出,谢谢. 启动流程 调用settings_init()设定初始化参数 从启动命令中读取参数来设置setting值 设定LIMIT参数 开始 ...

  8. ActionBar修改字体颜色

    style: <resources xmlns:android="http://schemas.android.com/apk/res/android"> <!- ...

  9. WinRAR的命令行模式用法介绍

    因工作中要对数据打包,顺便研究了下WinRAR的命令行模式,自己写了些例子,基本用法如下: 测试压缩文件准备:文件夹test_data,内部包含子文件夹,分别存放了一些*.log和*.txt文件. 测 ...

  10. [转]推荐highcharts学习网址

    highcharts学习网址1:http://www.hcharts.cn/docs/index.php?doc=basic(百度highcharts中文教程即可) highcharts学习网址2:h ...