Spring整合MyBatis下@Transactional无效解决方案

场景复现

Spring + MyBatis 整合,将事务交给 Spring 处理,配置如下:


    
    
    



    



    
    



    
        
    




业务调用过程:

@Override
public Object bus() {
    try {
		//前置处理
	
        // 事务提交
        trans();
 
		// 后置处理
    } catch (Exception e) {
        log.error("error : {}", e);
    }
    return rsp;
}

@Transactional
private void trans() {
    // 多条SQL
}

bus调用trans完成事务提交,按道理这样实现没什么毛病,但是问题是事务不起作用,有心的可以先思考一下为什么不起作用。

问题分析

以上配置存在如下问题:

  1. @Transactional 只能应用在作用于为public的方法上,其它的无效,也不会抛出异常,由AOP的触发机制决定的;
  2. @Transactional 只作用于外部调用的情况下(触发AOP),函数内部调用也无效;
  3. SpringMVC扫描时也有可能覆盖(这里没出现,如:applicationContext.xml设置了component-scan,spring-mvc.xml也component-scan了同一个包,也会导致覆盖无效);

解决方案

有如下四种解决方案:

1. 操作MyBatis的sqlSession

sqlSession.rollBack();
...
sqlSession.commit();

无效,因为整合了spring-mybatis,已将事务交于spring处理,此法不通

2. 直接在bus上@Transactional

简单易用,缺点是将事务拉长,影响巨大,如果方法体中有RPC,那绝对不能用这种方法

3. 使用编程式事务

实现如下:

1.简单封装事务助手类

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 * @author 黄平财
 * @date 2017/11/27 16:46
 * @email [email protected]
 */
public class TransactionHelper {

    public static final String TRANSACTIONAL_BEAN_ID = "transactionManager";

    private PlatformTransactionManager transactionManager;

    // 记录事务状态
    private TransactionStatus status;

    // 可以在外部定义事务规则
    private DefaultTransactionDefinition definition;

    /**
     * 默认规则创建事务过程
     *
     * @return
     */
    public static TransactionHelper newInstance() {
        return new TransactionHelper();
    }

    /**
     * 根据事务规则创建事务
     *
     * @param definition
     * @return
     */
    public static TransactionHelper newInstance(DefaultTransactionDefinition definition) {
        return new TransactionHelper(definition);
    }

    private TransactionHelper(DefaultTransactionDefinition definition) {
        this.definition = definition;
        transactionManager = (PlatformTransactionManager) SpringContextHolder.getBean(TRANSACTIONAL_BEAN_ID);
    }

    private TransactionHelper() {
        this(new DefaultTransactionDefinition());
        this.definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    }


    /**
     * 开启事务
     */
    public void begin() {
        status = transactionManager.getTransaction(definition);
    }

    public void rollBack() {
        transactionManager.rollback(status);
        finish();
    }

    public void commit() {
        transactionManager.commit(status);
        finish();
    }

    private void finish() {
        definition = null;
        status = null;
        transactionManager = null;
    }
}

2.持有SpringContext对象

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;

/**
 * @author 黄平财
 * @date 2017/11/27 17:15
 * @email [email protected]
 */
@Component
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext APPLICATION_CONTEXT;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.APPLICATION_CONTEXT = applicationContext;
    }

    public static Object getBean(String name) {
        return APPLICATION_CONTEXT.getBean(name);
    }

    public static Object getBean(String name, Class clazz) {
        return APPLICATION_CONTEXT.getBean(name, clazz);
    }

    @PreDestroy
    public void destroy() {
        SpringContextHolder.APPLICATION_CONTEXT = null;
    }
}

3.使用

private void trans() {
    TransactionHelper helper = TransactionHelper.newInstance();
    try{
        helper.begin();
        // 多条SQL
        helper.commit();
    }catch (Exception e){
        if(null!=helper){
            helper.rollBack();
        }
        throw e;
    }
}

4.使方法走AOP代理

((XXService)AopContext.currentProxy()).trans()

结语

第四种方案较为简介靠谱.

引用

静态持有ApplicationContext:https://www.cnblogs.com/wcyBlog/p/4657885.html

你可能感兴趣的:(Spring)