不要把事务控制放在Http请求开始的地方 javamonkey 原创 更新:2008-04-01 22:48:58 版本: 1.0 第一次知道有这么一种控制事务的方法是在一个使用Hibernate的java项目里程序的架构大概如下,创建一个ServletFilter,处理所有Web请求,在这个ServletFilter里通过调用SessionFactory得到Session代码如下: public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { Session session = null; try { session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); RequestContext.put(session); chain.doFilter(request, response); session.getTransaction().commit(); session.close(); } catch(Throwable t) { HibernateUtil.close(session) } 这样管理事务的方法有很大的问题,一是事务的结束时间不但依赖于业务方法执行需要的时间,还取决于JSP 执行时间的长短,如果客户端网络很慢,JSP需要花很长时间才执行完内容的output,那么,这样的事务可能会花费额外的数秒中,那么,很可能出现潜在 的问题:如长时间锁表导致其他用户不可用;性能变得不好等。所以,还是建议把事务管理放在业务方法里面是比较好的. 这样在 ServletFilter初始化Session,并在Filter方法执行完毕后关闭Sesion还有一个更常见的应用,那就是便于使用 Lazy load.你可以在你的业务方法里,JSP里任意使用导航到另外一些对象那里,Hibernate会自动根据配置文件去从数据库Load相关对 象。这样做极大的方便程序编写,一是按需所取,你可以从一个User对象几乎导航到系统所有对象.二是Hibernate 本身load策略使用不方便, 只能配好,不能根据需要指定。如用户与好友关系,当用户登录,系统只需要取出用户自己的信息显示就可,根本不需要相关对象,但另外一个需求显示用户的好友 列表则需要Load。因此开发人员干脆不做任何需要设定,全设置Lazy load,在按照上面的Filter来处理 即使你把关于事务处理的代码放在业务层去控制而只是为了Lazy Load方便,我相信这样做还是有问题,原因有俩个 1) 系统层次不分明,原来经典的分层架构中是业务层负责处理业务数据,但现在 JSP也会从数据库里取数据(通过Hibernate)2) 接口设计过于粗糙,如前面说的,所有系统都可以只提供一个业务接口,getUser(),然后,让其他人去使用导航去取别的想要对象,如在JSP里 <% out.println(user.getFriend()[0].getVistLog()[0]); %> <% out.println(user.getPhoto().getPhotoMeta().getPath()); %> 3) 爆发性能问题,越来越多的项目里看到了满页甚至俩三页的 SQL输出日志,而执行的业务逻辑取仅仅是联合三,四个表取些数据,过于依赖Hibernate Lazy load的架构必然滋生性能问题(而这在使用纯JDBC的情况下不可能发生)所以,我认为不要为了迎合Hibernate的目前的Lazy load方式使用文章刚开始用的例子。得不偿失,也许只适合一些Demo系统或者根本不用关系客户感受的系统那如何解决Lazy Load问题呢,这有些个人建议和想法1) 数据还是在业务层一次取出,对于复杂的操作,可以考虑用SQL语句,对于简单的操作,你可以显示的调用一下导航,如user.getPhoto(),这么做对性能没有帮助,但至少能保证良好的接口设计和让别人明白你的意图。2)更改Hibernate的实现机制,支持"智能化 Load",如下面的例子 List list = session.createQuery("from user -- user.friend.vistlog --"); -- user.friend.vistlog -- 为一普通SQL注释 .意思是告诉 Hibernate你也需要vistlog对象 ,让Hibernate自己去考虑如何优化 List list = session.createQuery("from user "); 则只是按照默认策略来Load对象 当 然,更改Hibeernate实现简直是不可能做到的事情,只能希望Hibernate社区哪天加上新的实现.如果你自己要做,我的建议是做个伪实现,即 不优化SQL,只是简单的根据注释-- user.friend.vistlog -- 来Load相关对象,这已经是偏离此文的主体了。 总的来说,我并不认为在Http请求出来初始化HibernateSession 和在Http结束前关掉 Session 是一个很好的方法,这不单是技术上有潜在的问题,而且,导致整个架构不够明晰,导致开发人员懒于写优秀的代码。 <script type="text/javascript"><!-- google_ad_client = "pub-7390275636631344"; google_ad_width = 728; google_ad_height = 90; google_ad_format = "728x90_as"; google_ad_type = "text_image"; google_ad_channel ="5095444487"; google_color_border = "336699"; google_color_bg = "FFFFFF"; google_color_link = "0000FF"; google_color_url = "008000"; google_color_text = "000000"; //--></script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/expansion_embed.js"></script> <script src="http://googleads.g.doubleclick.net/pagead/test_domain.js"></script> <script>window.google_render_ad();</script> 版权声明 给作者写信 本篇文章对您是否有帮助? 投票: 是 否 投票结果: 11 1 作者其它文章: 总结:非主流存储数据方式预测一下几种技术和工具走向使用Quartz实现任务调度和调度管理Java异常处理:如何避免重复打印异常&如何友好的显示错误信息给终端用户Java生成Word文档的简单方法 作者全部文章 查看作者的Blog 评论人:xyz20003 发表时间: Tue Apr 01 00:45:47 CST 2008 说的不错,OpenSessionInView的确有这些隐患。但是因为太方便了,去掉osiv,hibernate就不好用了,本来就是lazy load + cache造成超越jdbc的优点,要是把这些关闭了,实在看不出还有什么使用hibernate的必要,就为了save的时候用javabean何不考虑ibatis呢?连 接超时导致session无法释放,的确非常唬人,实际上有一种用apache做前端,把网速慢的连接隔离掉的方法,让osiv只从apache到 tomcat起作用,如果客户网速太慢,则让apache去负责等待,不会出现session一直无法释放问的问题。(高并发访问情况待验证)没有两全其美的方法,诸君任选吧。osiv也只是一种开发手段而已。 评论人:xyz20003 发表时间: Tue Apr 01 00:46:49 CST 2008 你这里的txn控制太弱了,连异常回滚都没有,参考spring的osiv好好写一下吧。 评论人:270996853 发表时间: Tue Apr 01 14:44:15 CST 2008 评论人:championcgh 发表时间: Tue Apr 01 15:58:48 CST 2008 好东东啊 评论人:javamonkey 发表时间: Tue Apr 01 22:53:58 CST 2008 增加了处理事务的一个方法HiberanteUtil.close(session)更多关于Open Session in View可以参考此文http://www.hibernate.org/43.html。 评论人:zf534685796 发表时间: Tue Apr 15 20:17:58 CST 2008 sdf 评论人:yan55667 发表时间: Thu Aug 21 14:54:43 CST 2008