1、SSH整合以后事务问题和总结java
1.引入问题:DAO层测试git
假设将User对象设置为懒加载模式,在dao层使用load方法。github
注意,注释不要放开。web
使用以下的代码块进行测试:spring
会报错:no session。sql
为何会没有session呢,由于在代码apache
User user=(User) hibernateTemplate.load(User.class, id);
执行完成以后session就已经关闭了。缓存
2.Service层测试安全
(1)Service层代码:服务器
DAO层代码:
测试代码:
运行结果:
出现的错误结果和上面彻底相同。
(2)解决错误
将DAO层中的代码注释去掉或者将Service中的代码注释去掉。
注释的代码做用是打印结果,只是一个System.out.println方法的执行;虽然如此,程序运行再也不有问题。
3.缘由分析
(1)为何测试DAO不管怎么测试都会报出异常?
由于DAO层调用完成getUserById方法以后session就立刻关闭了,因此执行下一句代码的时候即便是打印输出的代码也会报错。
(2)为何测试Service的时候若是没有去掉注释的话,就会出错?
由于去掉注释的话,Service层调用完成getUserById方法以后Session就会关闭,效果和在DAO层是彻底相同的,因此结果和在DAO层是彻底相同的。报错的信息彻底相同。
(3)为何Service层去掉注释以后就不报错了?
Service层去掉注释以后因为方法尚未退出,因此会将数据保存到User对象中;因此数据已经保存到了内存中,这样就不会报错了。
(4)在DAO层去掉注释以后会不会报错?
去掉DAO层的注释以后的运行结果:
结果是没有报错,为何,DAO层调用完成查询的方法以后不是会当即关闭session吗?
结果是没有关闭Session,缘由是在Service层调用的方法,该方法具备事务,并且该事务的范围扩展到了DAO层。最根本的缘由是在Spring配置文件中的配置:
propagation属性值是默认的,因此在Service层中方法执行的时候,执行的全部方法都将会带有事务,直到方法结束,事务也会随之结束,所以DAO层才没有当即结束事务(关闭session)。
4.总结SSH整合以后事务管理的范围
(1)若是当前执行的方法没有事务环境,当执行完成this.hibernateTemplate中的方法以后session当即关闭。
(2)若是当前执行的方法有事务环境,当事务环境的方法被调用完成以后session关闭。
5.经过以上的分析和总结能够获得若是直接在Action中调用Service层中的方法,若是出现异常,确定不能回滚,例以下面中的测试代码:
很明显,这里有/0的异常,因此若是事务可以回滚,则不会发出insert的sql语句。可是结果倒是:
因此事务回滚失败了。缘由就是Service对象中的每个方法执行完成以后都会当即关闭session,事务也随之消失。相同Service中的方法调用屡次或者相同Service中的不一样方法执行的时候必定不会在同一个事务中。
2、解决SSH整合以后的事务问题
解决方法就是使用OSIV模式(Open Session In View),即在MVC中的View层就开启事务,这样就扩展了事务的范围。
只是在web.xml文件中加上以下的配置便可:
<!-- 使用OSIV模式解决事务回滚问题,只须要添加一个监听器就能够了 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
也就是说只是增长了一个过滤器而已,应当注意该过滤器放置的位置应当在下面的配置上面:
<filter> <filter-name>strutsFilter</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>strutsFilter</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
不然添加的过滤器不生效。完整的web.xml文件配置以下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="3.0" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 7 8 9 <!-- 使用OSIV模式解决事务回滚问题,只须要添加一个监听器就能够了 --> 10 <filter> 11 <filter-name>OpenSessionInViewFilter</filter-name> 12 <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> 13 </filter> 14 <filter-mapping> 15 <filter-name>OpenSessionInViewFilter</filter-name> 16 <url-pattern>*.action</url-pattern> 17 </filter-mapping> 18 19 <filter> 20 <filter-name>strutsFilter</filter-name> 21 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 22 </filter> 23 <filter-mapping> 24 <filter-name>strutsFilter</filter-name> 25 <url-pattern>*.action</url-pattern> 26 </filter-mapping> 27 28 29 30 <listener> 31 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 32 </listener> 33 <context-param> 34 <param-name>contextConfigLocation</param-name> 35 <param-value>classpath:spring/applicationContext.xml</param-value> 36 </context-param> 37 38 <welcome-file-list> 39 <welcome-file>index.jsp</welcome-file> 40 </welcome-file-list> 41 </web-app>
最后,使用OSIV模式应当注意的事项:
1.使用OSIV模式以后,sessio的打开被提早了,session的关闭被延后了,这样就解决了懒加载引发的异常的问题。
2.两个过滤器,OSIV的过滤器必须在struts2以前。
使用OSIV模式的缺点:
由于Session的关闭被延迟了,因此hibernate的一级缓存在session中,因此会致使大量的缓存数据长期保存在了内存中。
3、Struts2流程回顾
1.启动服务器的时候干了两件事情。
* dispatcher = init.initDispatcher(config);该句代码加载了各类各样的配置文件。
* init.initStaticContentLoader(config, dispatcher); 该句代码完成了静态注入。
2.当过滤器拦截到一个请求的时候作了什么事情
查看doFilter方法中的源代码。
(1)建立AciontContext对象。
建立ActionContext对象的同时会同时建立ValueStack对象,并且建立ValueStack对象在前,建立ActionContext对象在后。
两个对象同时建立,并且两个对象维护同一个Map对象,缘由是建立ActionContext的时候使用的参数是ValueStack对象的context成员变量。
经过代码ActionContext.setContext(tx);便可以将ActionContext放置到ThreadLocal中了,这样数据就安全了,下面是代码追踪。
(2)建立ActionProxy对象
init方法中执行了建立了对应的Action和全部的拦截器。
最后在invocation的invoke方法中执行全部的拦截器、执行当前请求的action、执行结果集。
4、Struts2的完整流程图
最后附上项目源代码:https://github.com/kdyzm/day53_ssh_oa