• 【Java EE 学习 54】【OA项目第一天】【SSH事务管理不能回滚问题解决】【struts2流程回顾】


    一、SSH整合之后事务问题和总结

      1.引入问题:DAO层测试

        假设将User对象设置为懒加载模式,在dao层使用load方法。

         

        注意,注释不要放开。

        使用如下的代码块进行测试:

        会报错:no session。

        

        为什么会没有session呢,因为在代码

    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中的不同方法执行的时候一定不会在同一个事务中。

    二、解决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>
    web.xml

      最后,使用OSIV模式应当注意的事项:

        1.使用OSIV模式之后,sessio的打开被提前了,session的关闭被延后了,这样就解决了懒加载引起的异常的问题。

        2.两个过滤器,OSIV的过滤器必须在struts2之前。

      使用OSIV模式的缺点:

        因为Session的关闭被延迟了,所以hibernate的一级缓存在session中,所以会导致大量的缓存数据长期保存在了内存中。

    三、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、执行结果集。

        

    四、Struts2的完整流程图

      

    最后附上项目源代码:https://github.com/kdyzm/day53_ssh_oa

  • 相关阅读:
    USACO Broken Necklace 通过了
    USACO Broken Necklace
    推荐顺序ACM
    usaco暂时无法访问
    格式
    稳定排序
    归并排序
    浅析Struts1和Struts2的Action线程安全问题
    判别式模型与生成式模型的区别
    远景能源一面
  • 原文地址:https://www.cnblogs.com/kuangdaoyizhimei/p/4865715.html
Copyright © 2020-2023  润新知