原文地址: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. Unable to extract 64-bitimage. Run Process Explorer from a writeable directory

    Unable to extract 64-bitimage. Run Process Explorer from a writeable directory When we run Process E ...

  2. IBatis.Net项目数据库SqlServer迁移至Oracle经验

    最近完成了一个(IBatis.Net+MVC)项目的数据库+代码迁移工作,可把我折腾得~~~ IBatis.Net是一个ORM框架,具体介绍可以问度娘.我之前没用ORM框架使用经验,所以这一路我不是走 ...

  3. AJAX请求详解 同步异步 GET和POST

    AJAX请求详解 同步异步 GET和POST 上一篇博文(http://www.cnblogs.com/mengdd/p/4191941.html)介绍了AJAX的概念和基本使用,附有一个小例子,下面 ...

  4. reason: '[<__NSDictionary0 0x7fda88f00c90> setValue:forUndefinedKey:]: this class is not key value c

    reason: '[<__NSDictionary0 0x7fda88f00c90> setValue:forUndefinedKey:]: this class is not key v ...

  5. BitSet构造函数的两种特例

    C++11之后,bitset的构造函数新加了两种形式: bitset<bits>::bitset (const string& str, string::size_type str ...

  6. HSDB - HotSpot debugger

    HSDB 是专门用于调试 HotSpot VM 的调试器,它是一个图形化界面.与之对应的还有个 CLHSDB-Command Line HotSpot Debugger,命令行调试界面.下面是启动命令 ...

  7. Meet Sccot Guthrie in Shanghai

    9月16日,有幸和其他9位MVP一起在上海和微软全球副总裁Sccot Guthrie.开发工具部门Somasegar.微软中国申元庆见面,聊关于Azure和开发方面的话题.同时,由于本人目前对物联网( ...

  8. Linux 克隆虚拟机引起的“Device eth0 does not seem to be present, delaying initialization”

    虚拟机Vmware上克隆了一个Red Hat Enterprise Linx启动时发现找不到网卡,如下所示,如果你在命令窗口启动网络服务就会遇到"Device eth0 does not s ...

  9. 设计模式C#实现(十三)——享元模式(蝇量模式)

    意图 0 适用性 1 结构 2 实现 3 效果 4 参考 5 意图 运用共享技术有效地支持大量细粒度的对象. 适用性 当以下情况都成立时使用: 一个程序使用了大量的对象 完全由于使用大量对象造成很大存 ...

  10. 【转载】 Java线程面试题 Top 50

    Java线程面试题 Top 50 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员 的欢迎.大多数待遇丰厚的J ...