11.1.1. 操作单元(Unit of work)
首先,别用session-per-operation这种反模式了,也就是说,在单个线程中, 不要因为一次简单的数据库调用,就打开和关闭一次Session
!数据库事务也是如此。 应用程序中的数据库调用是按照计划好的次序,分组为原子的操作单元。(注意,这也意味着,应用程 序中,在单个的SQL语句发送之后,自动事务提交(auto-commit)模式失效了。这种模式专门为SQL控制台操作设计的。 Hibernate禁止立即自动事务提交模式,或者期望应用服务器禁止立即自动事务提交模式。)数据库事务绝不是可有可无的,任何与数据库之间的通讯都必须在某个事务中进行,不管你是在读还是在写数据。对读数据而言,应该避免auto-commit行为,因为很多小的事务比一个清晰定义的工作单元性能差。后者也更容易维护和扩展。
在多用户的client/server应用程序中,最常用的模式是 每个请求一个会话(session-per-request)。 在这种模式下,来自客户端的请求被发送到服务器端(即Hibernate持久化层运行的地方),一 个新的Hibernate Session
被打开,并且执行这个操作单元中所有的数据库操作。 一旦操作完成(同时对客户端的响应也准备就绪),session被同步,然后关闭。你也可以使用单 个数据库事务来处理客户端请求,在你打开Session
之后启动事务,在你关闭 Session
之前提交事务。会话和请求之间的关系是一对一的关系,这种模式对 于大多数应用程序来说是很棒的。
实现才是真正的挑战。Hibernate内置了对"当前session(current session)" 的管理,用于简化此模式。你要做的一切就是在服务器端要处理请求的时候,开启事务,在响应发送给客户之前结束事务。你可以用任何方式来完成这一操作,通常的方案有ServletFilter
,在service方法中进行pointcut的AOP拦截器,或者proxy/interception容器。EJB容器是实现横切诸如EJB session bean上的事务分界,用CMT对事务进行声明等方面的标准手段。假若你决定使用编程式的事务分界,请参考本章后面讲到的Hibernate Transaction
API,这对易用性和代码可移植性都有好处。
在任何时间,任何地方,你的应用代码可以通过简单的调用sessionFactory.getCurrentSession()
来访问"当前session",用于处理请求。你总是会得到当前数据库事务范围内的Session
。在使用本地资源或JTA环境时,必须配置它,请参见第 2.5 节 “上下文相关的(Contextual)Session”。
有时,将Session
和数据库事务的边界延伸到"展示层被渲染后"会带来便利。有些serlvet应用程序在对请求进行处理后,有个单独的渲染期,这种延伸对这种程序特别有用。假若你实现你自己的拦截器,把事务边界延伸到展示层渲染结束后非常容易。然而,假若你依赖有容器管理事务的EJB,这就不太容易了,因为事务会在EJB方法返回后结束,而那是在任何展示层渲染开始之前。请访问Hibernate网站和论坛,你可以找到Open Session in View这一模式的提示和示例。