| 不要把事务控制放在Http请求开始的地方.. |
|
| 作者是 Administrator | |
| 2008-04-02 07:10:44 | |
|
第一次知道有这么一种控制事务的方法是在一个使用Hibernate的java项目里 程序的架构大概如下, 创建一个ServletFilter,处理所有Web请求,在这个ServletFilter里通过调用SessionFactory得到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 是一个很好的方法,这不单是技术上有潜在的问题,而且,导致整个架构不够明晰,导致开发人员懒于写优秀的代码。 |
|
| 最近更新 ( 2008-04-02 07:10:44 ) |

