1、Spring+Hibernate 延迟加载和声明式事务处理最终解决方案一、 spring+hibernate 时使用 hibernate 的延迟加载功能是存在的问题:spring 是一个设计层面的框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring 的 orm 框架用来集成其他持久层框架,比如 hibernate,spring-orm 框架对hibernate 的 session 进行了封装,我们可以很方便的通过继承这个封装类 HibernateDAOSupport 并注入hibernate 的 sessionFactory 完成初始化,并
2、调用其内置对象 HibernateTemplate 的封装方法来调用 session 的API 而不用考虑到 session 的初始化和关闭以及事务处理等系统操作,这也是 AOP 思想的一种体现。Hibernate 的延迟加载功能是指获取某个实体对象时并不从数据库中加载他的关联对象,而在实际获取关联对象的时候才从数据库中加载,这样做很好的节省了数据库资源但是前提是必须保持 session 处于打开状态,在所有操作完成后再关闭。Spring 封装了 session 操作后很自然的要做到在方法调用的前后打开和关闭session,这样我们在通过 HibernateTemplate 的方法来获取实体对
3、象以后 session 就已经关闭了,而这时候在调用获取关联对象的方法的时候就会抛出异常:二、 如何解决1、 openSessionInViewInterceptor通过 spring 的控制层框架 spring-mvc 来处理控制层并通过拦截器 openSessionInViewInterceptor 改变spring 调用 session 的流程。这里介绍最简单的spring-mvc 的使用方法,首先和 struts 一样要定义一个 HttpServlet 来总体控制请求的处理类以及容器的初始化,struts 是 ActionServlet,Spring 是DispatcherServle
4、t,如图:如同 struts 的控制器都继承于 Action 类一样, spring的控制器都要实现 controller 接口我们通过注入的方式传入 usersDAO 进行数据操作(通常这个操作应该是放到 Service 层,这里为了方便讲解没有加入 Service 层) ,这个 DAO 是myeclipse 自动生成的原封不动,这里不再贴出来了。几句话的意思很清楚,调用 findByID 方法把 ID 为1000 的用户实体查出来,这个时候按道理 session已经关闭了,下面调用 user.getNewses 方法获取这个用户发布的新闻信息的一个 set 集合,打印条数,系统显示:没有报
5、 session 已经关闭的错误,原因是 spring-config 中作了配置,在配置之前我们要对 spring-mvc 做一些初步的了解。配合 DispatcherServlet 我们也要定义相关的映射,能够将请求跳转到对应的控制器,先看看 usersDAO然后是我们的控制器 LoginSpring,这里没有对跳转作处理,因为我们的重点不是 mvc 而是解决延迟加载。最后是我们的映射和针对这些控制器所配置的拦截器 opensessioninviewinterceptor这样配置了以后管理 session 的就不是我们的hibernatetemplate 了而是我们配置的拦截器,他会保持 s
6、ession 直到我们的控制器做完了所有的事情才关闭,也就是我们调用 user.getNewses 方法的时候还是打开的,因此能得到满意的结果。2、 openSessionInViewFilter当我们的系统的控制层要使用 struts 的时候,我们就不能使用 spring 的拦截器了,因个这个拦截器是基于 DispatcherServlet 的,我们只有在 web.xml 中配置过滤器来改变 session 的流程。首先简要介绍一下 struts+spring 集成,看看 struts 的配置文件:将每个 Action 的 type 都设定为org.springframework.web.s
7、truts.DelegatingActionProxy,并在最下面配置 spring 的配置文件的路径,当我们要使用过滤器的时候这个配置文件必须只存放Action 的实际映射,下面是 spring-action.xml,里面只有一个 action:这里不需要指定 id,只需要指定别名,这个别名会自动匹配 struts 配置文件中对应的 path, DelegatingActionProxy 会自动从插件所配置的spring 配置文件中寻找匹配的 bean 并实例化,当然也会完成注入的过程。知道怎么将两个框架正和使用以后,我们来配置过滤器,见 web.xml:这里使用过滤器的时候一定要附上所有
8、spring 配置文件并在 web 容器(tomcat)启动时加载和初始化。这里 spring-action.xml 存放的 Struts 的 Action 的映射,spring-config.xml 中存放其他的 bean。最后再看看这个 Action,和上面的一样的代码。这样的话当我们使用延迟加载的时候调用的就是HibernateTamplate 的代理类,能够让 Spring 在请求开始的时候打开 Session,响应结束前关闭Session,这样就不会存在 Session 关闭的错误了。但是当我们增删改的时候,又会出现下面的问题:为什么会有这样的问题呢,因为 Hibernate 有自己
9、的事务策略,我们在 Spring 的 OpenSessionInView 中打开 Session 是以只读的方式来管理事务,这样进行增删改的时候就会出现错误。如何解决,先看看Spring 的事务处理。三、 声明式事务处理声明式事务处理是 springAOP 思想的一个扩展,事务处理是一个典型的系统功能,因此通过将事务处理封装在一个切面中进行处理以分离具体业务操作和系统功能的方式是最好的设计层面的选择。首先我们要配置一个 PlatformTransactionManager 接口的一个实现用来控制事务流程(commit 和 rollback) , 如果我们是 spring+hibernate,框架集成的话,我们就要配置 hibernate 专用的 PlatformTransactionManager 实现:我们模拟一个实例就是两个银行之间的转账,MsBankDAO 民生银行 DAO;ZsBankDAO 招商银行DAO。这两个 DAO 都是用 myeclipse 生成,这里不作介绍,我们的业务逻辑对象 BankBO 的 transferMoney方法来完成这个转账操作:其中 if 语句中的两个操作就是一个典型的粗粒度事务,我们使用声明的方式来进行事务处理就无需在这里面加入任何关于事务的代码。既然是使用 AOP 代理 BO,我们就得有一个BankBOImpl 的代理类: