`
fancy888
  • 浏览: 38601 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

实现跨事务的Hibernate懒加载

阅读更多

      最近使用Eclipse插件制作一个项目管理工具,由于涉及的数据结构比较复杂,简单的展示一棵导航树就涉及到至少20个表关联查询(其中包含大量的自关联及循环关联),刚开始数据量较小时尚可应付,当运行一段时间后,数据量达到万级以后,性能就相当差了,仔细看了一下,由Hibernate翻译过来的一条SQL就显示了十屏以上,汗一个先,所以下定决心优化一下。

      首先想到是数据库层面的优化,由于数据结构已经固定,无法再通过增加删除表来优化,剩下的策略只能是加加索引,增加视图来减化复杂性(要命的是视图无法作更新操作),但收效甚微。

      其次想到的是使用Hibernate的二级缓存来优化性能,但由于CS应用的特点决定,数据操作一定是发生在并发的环境中,所以贸然打开二级缓存会带来更大的麻烦(所谓的脏数据),所以基本放弃了,试着针对几个常量表开了只读的二级缓存,但效果依然不明显。

      最后想到的是使用原来作WEB应用时常用的Open Session In View方式来作基于长事务的懒加载,但与WEB应用不同,WEB应用可以严格的界定事务边界,简单的加一个Filter就可以实现了,但CS架构的应用没有明显的边界,数据的查询和更新发生在整个生命周期的任何时刻,想破了头也没想到好的方法来实现,至此基本打算放弃了。

      这几天翻看Hibernate源码,发现Hibernate实现懒加载并不强制要求被加载对象与宿主对象必须在同一个事务中,只要有一个可用的Session,并且在这个Session的上下文(其实就是Session的一级缓存)中注册相应的持久类信息即可,而这个Session是否与被加载对象的宿主处在同一个事务中,Hibernate并不关心,基于此准备实现一个跨事务的懒加载机制来简化复杂业务下的大数据加载问题。

 

protected Object initialize(final Object proxy) {
	if (proxy instanceof HibernateProxy)
		return initializeHibernateProxy((HibernateProxy) proxy);
	else if (proxy instanceof PersistentCollection)
		return initializePersistentCollection((PersistentCollection) proxy);
	return proxy;
}

private Object initializePersistentCollection(final PersistentCollection persistentCollection) {
	if (persistentCollection.getRole() != null && !persistentCollection.wasInitialized() && ((AbstractPersistentCollection) persistentCollection).getSession() == null) {
		return getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				final SessionImplementor sessionImplementor = (SessionImplementor) session;
				final CollectionPersister collectionPersister = sessionImplementor.getFactory().getCollectionPersister(persistentCollection.getRole());
				sessionImplementor.getPersistenceContext().addUninitializedDetachedCollection(collectionPersister, persistentCollection);
				persistentCollection.setCurrentSession(sessionImplementor);
				persistentCollection.forceInitialization();
				sessionImplementor.getPersistenceContext().clear();
				persistentCollection.unsetSession(sessionImplementor);
				return persistentCollection;
			}
		});
	}
	return persistentCollection;
}

private Object initializeHibernateProxy(final HibernateProxy proxy) {
	final LazyInitializer lazyInitializer = proxy.getHibernateLazyInitializer();
	if (lazyInitializer.isUninitialized() && lazyInitializer.getSession() == null) {
		return getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				lazyInitializer.setSession((SessionImplementor) session);
				lazyInitializer.initialize();
				lazyInitializer.unsetSession();
				return proxy;
			}
		});
	}
	return proxy;
}

 

      按道理,为了尽可能的减少对原有结构的侵入,应该对所有实体类的GET方法作代理,判断数据是否已被加载,如果没加载,则调用initialize方法先加载数据后再返回,可是我至今没有发现Hibernate提供类似的Intercepter,应该是可以使用第三方的代理来实现,这块我再研究一下,目前的实现方式是对实体类的GET方法硬编码:

 

public ComponentTypeBean getComponentTypeBean() {
	return (ComponentTypeBean) initialize(componentTypeBean);
}

@SuppressWarnings("unchecked")
public Set<PageComponentBean> getPageComponentBeans() {
	return (Set<PageComponentBean>) initialize(pageComponentBeans);
}

 

      显然这样作可以实现预期效果,但并不理想,不知道Hibernate是否提供了实体类的GET代理,等有时间再翻一下文档。


      至此,基于跨事务的懒加载已经实现,当然方法可能用的并不是很正确,希望有高人再指点一下。经测试实际效果非常理想,总结一下使用跨事务懒加载:

      好处:

      1.原来长篇大论的HQL全部可以简化为单表查询了,换句话说再也不用费尽脑汁的去想需要写几个join fech了,只要查询出主表数据即可,随时随地调用GET方法即可实现从表数据的懒加载,彻底告别could not initialize proxy - no Session,could not initialize proxy - the owning Session was closed等异常。

      2.将大SQL拆成若干个单表查询的小SQL,在特定环境下会为显著提升系统性能,改善用户体验。

      坏处:

      1.会造成SQL量增大,用的不好反而会造成系统性能下降。

      2.与Open Session In View不同,跨事务懒加载破坏了Hibernte原有的缓存体系,无法发挥事务内缓存带来的性能提升。

原文地址: http://www.coolfancy.com/log/31.html

 

更多精彩原创文章请关注笔者的原创博客:http://www.coolfancy.com

 

分享到:
评论
5 楼 longgangbai 2010-07-15  
关于Hibernate的拦截机制和监听机制强大,通常可以实现你所说的CS拦截问题。我们公司全年的项目中财务报表日志中等多个地方使用,貌似可以解决,详细请看
http://topmanopensource.iteye.com/blog/538213
4 楼 wlwolf 2010-07-01  
fancy888 写道
iamlibo 写道
是不是可以考虑AOP方式呢?


直接使用Spring的AOP应该是行不通的,因为Hibernate内部在创建实体类的时候肯定不会使用Spring的工厂,Hibernate应该提供了自己的工厂,只是猜想。


spring 也提供对aspectj的支持的,并不需要使用bean容器就可以实现AOP的

可以使用编译时织入或者使用spring提供的加载时织入,只有使用Spring的运行时织入才会依赖Spring bean容器
3 楼 zhangshoukai 2010-07-01  
一定是小辉。
2 楼 fancy888 2010-05-10  
iamlibo 写道
是不是可以考虑AOP方式呢?


直接使用Spring的AOP应该是行不通的,因为Hibernate内部在创建实体类的时候肯定不会使用Spring的工厂,Hibernate应该提供了自己的工厂,只是猜想。
1 楼 iamlibo 2010-05-08  
是不是可以考虑AOP方式呢?

相关推荐

    Hibernate性能(缓存 延迟加载 事务 悲观 乐观锁).ppt

    性能------------缓存 延迟加载 事务 悲观 乐观锁

    Hibernate+中文文档

    3.6. Hibernate事务属性 3.7. 其他属性 3.8. Hibernate SQL方言 (hibernate.dialect) 3.9. Hibernate日志类别 3.10. JTA TransactionManagers 9.1. 继承映射特性(Features of inheritance mappings) 16.1. ...

    Hibernate实战(第2版 中文高清版)

     10.3.2 使用Hibernate非事务地工作   10.3.3 使用JTA的可选事务   10.4 小结   第11章 实现对话   11.1 传播Hibernate Session   11.1.1 Session传播的用例   11.1.2 通过线程局部传播   11.1.3 ...

    hibernate操作数据库笔记

    初始化Hibernate:在要使用Hibernate的类的方法中实例化Configuration对象并用Configuration对象的configure()方法将hibernate.cfg.xml中的配置加载到内存,即: Configuration config = new Configuration()....

    hibernate3.2中文文档(chm格式)

    3.6. Hibernate事务属性 3.7. 其他属性 3.8. Hibernate SQL方言 (hibernate.dialect) 3.9. Hibernate日志类别 3.10. JTA TransactionManagers 9.1. 继承映射特性(Features of inheritance mappings) 16.1. ...

    HibernateAPI中文版.chm

    3.6. Hibernate事务属性 3.7. 其他属性 3.8. Hibernate SQL方言 (hibernate.dialect) 3.9. Hibernate日志类别 3.10. JTA TransactionManagers 9.1. 继承映射特性(Features of inheritance mappings) 16.1. ...

    hibernate 教程

    延迟初始化(延迟加载)(Lazy Initialization) 6.6. 集合排序(Sorted Collections) 6.7. 使用&lt;idbag&gt;&lt;br&gt;6.8. 双向关联(Bidirectional Associations) 6.9. 三重关联(Ternary Associations) 6.10....

    Hibernate 中文 html 帮助文档

    1. Hibernate入门 1.1. 前言 1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第...

    Hibernate中文详细学习文档

    1. Hibernate入门 1.1. 前言 1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第...

    第24次课-1 Spring与Hibernate的整合

    Object get(Class entityClass, Serializable id):根据主键加载特定持久化类的实例 24.3 Spring对Hibernate的简化 24.3.3 HibernateTemplate的常用方法 Serializable save(Object entity):保存新的实例 void ...

    spring+hibernate代码实例集

    与spring 结合hibernate关系映射(父子,组合),事务,延迟加载。采用maven测试,hsqldb

    hibernate 体系结构与配置 参考文档(html)

    1. Hibernate入门 1.1. 前言 1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第...

    Hibernate教程

    Hibernate参考文档 目录 前言 1. 翻译说明 2. 版权声明 1. 在Tomcat中快速上手 1.1. 开始Hibernate之旅 1.2. 第一个持久化类 1.3. 映射cat 1.4. 与Cat同乐 1.5. 结语 2. Hibernate入门 2.1. 前言 2.2. 第...

    hibernate二级缓存使用范例

    Hibernate中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程...

    Hibernate_3.2.0_符合Java习惯的关系数据库持久化

    3.6. Hibernate事务属性 3.7. 其他属性 3.8. Hibernate SQL方言 (hibernate.dialect) 3.9. Hibernate日志类别 3.10. JTA TransactionManagers 9.1. 继承映射特性(Features of inheritance mappings) 16.1. ...

    hibernate+中文api

    1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. 映射Person类 1.3.2. 单向Set-based的关联 1.3.3. 使关联工作 1.3.4. 值类型的集合 1.3.5. 双向关联 1.3.6. 使双向连起来 1.4. 第三部分 - Event...

    hibernate精华教程

    1)配置环境,加载hibernate的jar文件,以及连接数据库连接使用的jar文件,并配置CLASSPATH环境变量。 2)写POJO类(普通的java类) 3)写hibernate所需的配置文件,hibernate.cfg.xml ,Xxxxx.hbm.xml 4)调用...

    hibernate

    延迟初始化(延迟加载)(Lazy Initialization) 6.6. 集合排序(Sorted Collections) 6.7. 使用&lt;idbag&gt;&lt;br&gt;6.8. 双向关联(Bidirectional Associations) 6.9. 三重关联(Ternary Associations) 6.10....

    hibernate总结

    Hibernate的检索方式:(查询/加载) 1. 通过OID加载 session.get(Users.class, 1); * 2. 通过HQL/SQL 检索 hibernate query language (面向对象的查询语言) * a) 不再操纵表,它操纵的是持久化类的对象 b) 面向...

    hibernate3.04中文文档.chm

    1.1. 开始Hibernate之旅 1.2. 第一个持久化类 1.3. 映射cat 1.4. 与Cat同乐 1.5. 结语 2. Hibernate入门 2.1. 前言 2.2. 第一部分 - 第一个Hibernate程序 2.2.1. 第一个class 2.2.2. 映射文件 2.2.3...

Global site tag (gtag.js) - Google Analytics