• Spring3声明式事务处理事务无法回滚rollback分析(annotation与xml配置混用)


    新项目试运行,DBA提示生产数据库一个表的事务20分钟都未提交,分析过程如下:

    1.查看日志log文件,最近20分钟是否有error日志;

    2.发现某表有insert错误日志,初步判断由该表插入异常,并且未做rollback操作;

    3.查看代码:该表的操作DAO、Service,事务处理为Spring的声明式事务处理,并控制到Service层;

    4.场景还原:

    打开Spring的debug日志(查看数据库conn打开、commit、rollback必须启用debug)

    模拟插入异常数据

    打断点测试。。。。

    5.异常日志重现,分析日志发现未有事务的任何处理(默认以来数据库的自动事务提交);

    6.问题原因初步判定为:Spring声明式事务代理失败

    代码分析:

    1. 配置文件

    因为事务配置在service层,直接看service的配置:

        <!-- 业务事务管理服务 -->
        <bean id="transactionManager_masopen" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource_masopen"></property>
        </bean>
    
        <!-- 【BIZ业务逻辑层Service事务代理模板 - Tx Proxy Template】 -->
        <bean id="txProxyService_masopen" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
            <property name="transactionManager" ref="transactionManager_masopen" />
            <property name="proxyTargetClass" value="false" />
            <property name="transactionAttributes">
                <props>
                    <prop key="get*">PROPAGATION_SUPPORTS,-Throwable,readOnly</prop>
                    <prop key="query*">PROPAGATION_SUPPORTS,-Throwable,readOnly</prop>
                    <prop key="find*">PROPAGATION_SUPPORTS,-Throwable,readOnly</prop>
                    <prop key="count*">PROPAGATION_SUPPORTS,-Throwable,readOnly</prop>
                    <prop key="update*">PROPAGATION_REQUIRED,-Throwable,ISOLATION_READ_COMMITTED</prop>
                    <prop key="insert*">PROPAGATION_REQUIRED,-Throwable,ISOLATION_READ_COMMITTED</prop>
                    <prop key="finish*">PROPAGATION_REQUIRED,-Throwable,ISOLATION_READ_COMMITTED</prop>
                    <prop key="batch*">PROPAGATION_REQUIRED,-Throwable,ISOLATION_READ_COMMITTED</prop>
                    <prop key="*">PROPAGATION_REQUIRED,-Throwable,ISOLATION_READ_COMMITTED</prop>
                </props>
            </property>
        </bean>
        
        <!-- 事务控制定义 -->
        <bean id="orderService_masopen" parent="txProxyService_masopen">
            <property name="target" ref="orderService"/>
        </bean>

    解释:

      a. 定义了一个处理事务的模板txProxyService_masopen,任何需要做事务处理的service类可直接使用该template

      (注意:区别于直接把所有的事务放XX目录下所有类的配置)

         -Throwable 就是对rollback的配置,一般也配置为-Exception

         -表示抛出该异常时需要回滚

        +表示即使抛出该异常事务同样要提交

        Throwable就所有exception的父类,只要有异常就会rollback

      b.orderService做了代理类orderService_masopen

    2. 代码

    public interface OrderService {
       。。。略。。。。
        
    @Service("orderService")
    public class OrderServiceImpl implements OrderService {
    
        /**
         * 订单数据库操作DAO
         */
        @Resource
        private OrderDao orderDao_masopen;
    
        。。。略。。。。

    定义了OrderService接口,然后OrderServiceImpl实现了该接口,并通过annotation设置资源name=orderService(上面1里面代理类ref的就是该bean)

    3.service接口调用

    /**
         * 订单数据库操作Service
         */
        @Resource
        private OrderService orderService_masopen;
        
        。。。略。。。。
        
        logger.info("落地日志信息:" + context.getLogs());
        int[] flag2 = logService_masopen.batchInsertLog(context.getLogs());
        int[] updateFlag = checkBatchUpdateResult(flag2);
        logger.info("日志记录总数:{},更新成功:{},更新失败:{}", context.getLogs().size(), updateFlag[0], updateFlag[1]);

    其他业务类中注入该service,并在业务逻辑中调用(典型的短事务)

    问题来了:

      正常注入了service

      @Resource
      private OrderService orderService_masopen;

      调用该资源没有事务处理,原因在哪???

    4. 单步调试

      调用该方法时,查看被代理的service,发现不是被代理的对象:

    分析:

    接口:OrderService

    实现类:OrderServiceImpl

    代理类:orderService_masopen

    调用注入:

    @Resource
    private OrderService orderService_masopen;

    很明显,问题出在注入该service的地方,通过resource自动注入的OrderService为它的实现类OrderServiceImpl,而不是代理类orderService_masopen

    首先:

    spring中,声明了2个bean:实现类OrderServiceImpl、代理类orderService_masopen

    可是注入时候,只识别了实现类,没有识别代理类

    5. 修改方案

     在Spring中值声明一个代理类,防止识别错误 

      a. 第一步:移除实现类OrderServiceImpl的annotation

    //@Service("orderService") --移除
    public class OrderServiceImpl implements OrderService {
    
        。。。略。。。。

      b.第二步:配置文件中不使用ref指定bean,直接写死bean路径,

    <!-- 事务控制定义 -->
    <bean id="orderService_masopen" parent="txProxyService_masopen">
        <property name="target" >
            <bean class="com.shengpay.masopen.domain.service.impl.OrderServiceImpl"></bean>
        </property>
    </bean>

      测试并查看日志,成功使用代理类:注意标红的$Proxy12

    6. 问题原因总结

    为何Spring声明了2个Bean,注入时候偏偏不能够识别代理的bean呢。

    错误就出在resource这个annotaion的使用上:

    @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入。
    @Resource装配顺序 
    (1). 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常;
    (2). 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常;
    (3). 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常;
    (4). 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

    如果仍旧声明两个bean,有仍要使用resource注入,只要简单指定name即可,准确的识别为代理类,代码如下:

    /**
     * 订单数据库操作Service
     */
    @Resource(name="orderService_masopen")
    private OrderService orderService_masopen;
  • 相关阅读:
    jQuery知识点小结
    Java并发(二):基础概念
    Java并发(一):基础概念
    Spring AOP初步总结(二)
    pycharm建立软连接
    python中安装pycurl(想要使用Tornado提供的客户端做爬虫 客户端里需要先安装pycurl)
    python2&python3 蛋疼的编码问题
    02-Elasticsearch的核心概念
    python-列表或元组去重
    python-enumerate函数
  • 原文地址:https://www.cnblogs.com/huahua035/p/5443808.html
Copyright © 2020-2023  润新知