Spring事务使用注意事项

Spring事务注意事项 

Spring有两种事务操作方式。
1.采用编程式事务。
2.采用注解声明式事务。
编程式事务依赖于Spring事务管理的两个核心类。分别式:
1. PlatformTransactionManager
2. TransactionTemplate(推荐使用)
PlatformTransactionManager的使用  
1. 先注入DataSource到事务管理器中。
<!--DataSourceTransactionManager位于org.springframework.jdbc.datasource包下,数据源事务管理类,提供对单个javax.sql.DataSource数据源的事务管理,主要用于JDBC,Mybatis框架事务管理。 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" />
</bean> 
2. 使用
@Resource   private PlatformTransactionManager txManager;    
......    
//定义事务隔离级别,传播行为,     
DefaultTransactionDefinition def = new DefaultTransactionDefinition();       
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);       
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);       
//事务状态类,通过PlatformTransactionManager的getTransaction方法根据事务定义获取;获取事务状态后,Spring根据传播行为来决定如何开启事务     
TransactionStatus status = txManager.getTransaction(def);       
jdbcTemplate = new JdbcTemplate(dataSource);     
int i = jdbcTemplate.queryForInt(COUNT_SQL);       
System.out.println("表中记录总数:"+i);     
try {           
     jdbcTemplate.update(INSERT_SQL, "1");           
     txManager.commit(status);  //提交status中绑定的事务     
     } catch (RuntimeException e) {           
     txManager.rollback(status);  
     //回滚     
     }  
TransactionTemplate的使用该类继承了接口DefaultTransactionDefinition,用于简化事务管理,事务管理由模板类定义,
主要是通过TransactionCallback回调接口或TransactionCallbackWithoutResult回调接口指定,
通过调用模板类的参数类型为TransactionCallback或TransactionCallbackWithoutResult的execute方法来自动享受事务管理。
TransactionTemplate模板类使用的回调接口:
1. TransactionCallback:通过实现该接口的“T doInTransaction(TransactionStatus status) ”方法来定义需要事务管理的操作代码;
TransactionCallbackWithoutResult:继承TransactionCallback接口, 
2. 提供“void doInTransactionWithoutResult(TransactionStatus status)”便利接口用于方便那些不需要返回值的事务操作代码。
//构造函数初始化TransactionTemplate TransactionTemplate template = new TransactionTemplate(txManager); 
template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);   
//重写execute方法实现事务管理 
template.execute(new TransactionCallbackWithoutResult() 
{  @Override  protected void doInTransactionWithoutResult(TransactionStatus status) 
{   jdbcTemplate.update(INSERT_SQL, "饿死");   
//字段sd为int型,所以插入肯定失败报异常,自动回滚,代表TransactionTemplate自动管理事务  
}} );
声明式事务声明式事务操作简单,无侵入式,与业务代码分开。声明式事务也有两种实现方式。
1. 采用Spring的<tx:advice>定义事务通知与AOP配置实现。
2. 通过@Transactional实现。

<!-- 
<tx:advice>定义事务通知,用于指定事务属性,其中“transaction-manager”属性指定事务管理器,并通过<tx:attributes>指定具体需要拦截的方法
    <tx:method>拦截方法,其中参数有:
    name:方法名称,将匹配的方法注入事务管理,可用通配符
    propagation:事务传播行为,
    isolation:事务隔离级别定义;默认为“DEFAULT”
    timeout:事务超时时间设置,单位为秒,默认-1,表示事务超时将依赖于底层事务系统;
    read-only:事务只读设置,默认为false,表示不是只读;
    rollback-for:需要触发回滚的异常定义,可定义多个,以“,”分割,默认任何RuntimeException都将导致事务回滚,而任何Checked Exception将不导致事务回滚;
    no-rollback-for:不被触发进行回滚的 Exception(s);可定义多个,以“,”分割;
 -->
<tx:advice id="advice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 拦截save开头的方法,事务传播行为为:REQUIRED:必须要有事务, 如果没有就在上下文创建一个 -->
        <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="" read-only="false" no-rollback-for="" rollback-for=""/>
        <!-- 支持,如果有就有,没有就没有 -->
        <tx:method name="*" propagation="SUPPORTS"/>
    </tx:attributes>
</tx:advice>

<!-- 定义切入点,expression为切人点表达式,如下是指定impl包下的所有方法,具体以自身实际要求自定义  -->
<aop:config>
    <aop:pointcut expression="execution(* com.kaizhi.*.service.impl.*.*(..))" id="pointcut"/>
    <!--<aop:advisor>定义切入点,与通知,把tx与aop的配置关联,才是完整的声明事务配置 -->
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>

注意点:
1.@Transactional 注解由于原理决定了他只能作用于public方法中,而这里改为private,就完全被忽略无视了。
2.必须要将方法写到另一个类中,而且要通过spring的注入方式进行调用才可以,因为因为Spring是基于CGLIB等AOP方法实现,可以通过生成动态代理类或者@Resource引用自己。


3.<tx:annotation-driven>一共有四个属性如下,
mode:指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理 
proxy-target-class:如果为true,Spring将创建子类来代理业务类;如果为false,则使用基于接口的代理。(如果使用子类代理,需要在类路径中添加CGLib.jar类库) 
order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制事务切面在目标连接点的织入顺序。 
transaction-manager:指定到现有的PlatformTransaction Manager bean的引用,通知会使用该引用 


4.如果想某个方法单独一个事务提交,可以将事务的传播级别设置为:

@Transactional(propagation = Propagation.REQUIRES_NEW)

5.类上的Transactional注解是全量被方法上的注解替换的,所以,以下方式是不会抛出异常的。因为Spring的事务默认只有在发生运行时异常即:RunTimeException时才会发生事务,如果一个方法抛出Exception或者Checked异常Spring的事务并不会回滚。

@Slf4j
@Service
@Transactional(rollbackFor = {Exception.class},  transactionManager = "xdTransactionManager")
public class WcPayServiceImpl implements WcPayService {
    @Resource
    private WxPayService wxService;


    @Transactional(transactionManager = "xdTransactionManager")
    @Override
    public Object invoke(JfappTransRequest request) throws Exception {
        throw new Exception("test");
    }

6. 如果想在当前类中让@Transactional生效,可以自己生成代理类。也可以注入当前类,如下所示。

@Slf4j
@Service
public class WcPayServiceImpl implements WcPayService {
    //注入当前类
     
    @Resource
    private WcPayService wcPayService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String handlerOrderNotifyResult(WxPayOrderNotifyResult notifyResult, boolean isNotify) {
         
    }

    //当前类中使用
    String strRet = wcPayService.orderNotifyResultHandler(notifyResult);
}

7. TransactionSynchronizationManager先提交事务,后异步操作,记得只有先有事务才能提交事务,要加入@Transactional

最开始需求,在修改之后提交一个异步请求,最开始的处理方式,伪代码

@Transactional
    public void createActivity(){
        createActivity();//新增操作
        updateOrder();//修改操作
        activityOrderAsyncService.activityOrderPayCallBack(activityReportOrder);
    }
@Service
public class ActivityOrderAsyncServiceImpl implements ActivityOrderAsyncService {
 
    @Async
    public void activityOrderPayCallBack(ActivityReportOrder activityReportOrder) throws Exception {
        ...........报错了
    }
}


注入代码以及接口省略......

按照常理来说一切都没问题,但报错,因为有事务,所以全部回滚了。目的是不管异步处理是否报错,新增操作和修改操作都可以成功

解决办法,提交当前事务并发送异步处理

@Transactional
    public void createActivity(){
        createActivity();//新增操作
        updateOrder();//修改操作
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                try {
                    activityOrderAsyncService.activityOrderPayCallBack(activityReportOrder);
                } catch (Exception e) {
                    log.error("操作失败", e);
                }
            }
        });
    }


@Transactional 

@Async

以上两个注解如果同时使用,在同一个类中@Async不会生效,@Async是基于动态代理的,需要代理对象。在@Transactional 方法中如果调用@Async的方法,如果两个方法都在同一个类中,那么调用@Async方法的是一个真实对象(this.xxxxxx()),this就是当前的对象,而非代理对象,@Async就不会生效,可以直接获取代理对象再调用@Async方法。或者把@Async的方法放在其他类中,注入调用方法的页是代理对象。
 

 https://blog.csdn.net/ypp91zr/article/details/105144895

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值