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;
原文地址:https://www.cnblogs.com/huahua035/p/5443808.html