最近使用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
分享到:
相关推荐
性能------------缓存 延迟加载 事务 悲观 乐观锁
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. ...
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的类的方法中实例化Configuration对象并用Configuration对象的configure()方法将hibernate.cfg.xml中的配置加载到内存,即: Configuration config = new Configuration()....
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. ...
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. ...
延迟初始化(延迟加载)(Lazy Initialization) 6.6. 集合排序(Sorted Collections) 6.7. 使用<idbag><br>6.8. 双向关联(Bidirectional Associations) 6.9. 三重关联(Ternary Associations) 6.10....
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. 第...
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. 第...
Object get(Class entityClass, Serializable id):根据主键加载特定持久化类的实例 24.3 Spring对Hibernate的简化 24.3.3 HibernateTemplate的常用方法 Serializable save(Object entity):保存新的实例 void ...
与spring 结合hibernate关系映射(父子,组合),事务,延迟加载。采用maven测试,hsqldb
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. 翻译说明 2. 版权声明 1. 在Tomcat中快速上手 1.1. 开始Hibernate之旅 1.2. 第一个持久化类 1.3. 映射cat 1.4. 与Cat同乐 1.5. 结语 2. Hibernate入门 2.1. 前言 2.2. 第...
Hibernate中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程...
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. ...
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...
1)配置环境,加载hibernate的jar文件,以及连接数据库连接使用的jar文件,并配置CLASSPATH环境变量。 2)写POJO类(普通的java类) 3)写hibernate所需的配置文件,hibernate.cfg.xml ,Xxxxx.hbm.xml 4)调用...
延迟初始化(延迟加载)(Lazy Initialization) 6.6. 集合排序(Sorted Collections) 6.7. 使用<idbag><br>6.8. 双向关联(Bidirectional Associations) 6.9. 三重关联(Ternary Associations) 6.10....
Hibernate的检索方式:(查询/加载) 1. 通过OID加载 session.get(Users.class, 1); * 2. 通过HQL/SQL 检索 hibernate query language (面向对象的查询语言) * a) 不再操纵表,它操纵的是持久化类的对象 b) 面向...
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...