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

来源:互联网 发布:电脑版阅读软件 编辑:程序博客网 时间:2024/06/07 02:39

场景复现

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

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">    <property name="dataSource" ref="dataSource" />    <!-- 自动扫描mapping.xml文件 -->    <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property></bean><bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">    <constructor-arg index="0" ref="sqlSessionFactory"/></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">    <property name="basePackage" value="com.sankuai.dao.mapper" />    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property></bean><bean id="donateTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    <property name="dataSource">        <ref bean="dataSource"/>    </property></bean><!-- 用注解来实现事务管理 --><tx:annotation-driven transaction-manager="donateTransactionManager" proxy-target-class="true"/>

业务调用过程:

@Overridepublic Object bus() {    try {        //前置处理        // 事务提交        trans();        // 后置处理    } catch (Exception e) {        log.error("error : {}", e);    }    return rsp;}@Transactionalprivate 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 hpingcai@gmail.com */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 hpingcai@gmail.com */@Componentpublic 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;    }}

结语

当然,这也是我当前能想到的较好的解决方案,代码写的有点简陋,如果有更合适的解决方案,欢迎留言探讨。

引用

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

原创粉丝点击