中间件不支持setautocommit(false)怎么办?

在实习期间遇到springmvc+spring+mybatis框架下,setautocommit(false)无法启动事务的问题。尝试了修改Spring的DataSourceTransactionManager、重写代码以及使用自定义注解+AOP的方法。最终通过AOP和DataSourceUtils获取数据库连接,在方法执行前手动开启事务,成功实现了事务的控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前提

最近在某个公司实习,框架使用的是springmvc+spring+mybatis,具体名字就不说了,所有事务都没有用无法回滚,导师叫我和db人员对接,我就写了一个Jdbc的原生的测试测试代码如下:

 try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //url写测试中间件的,这里我替换了
        String url="jdbc:mysql://localhost:3306/taotao?characterEncoding=utf-8";
        String username="root";
        String passwd="123456";
        Connection conn= null;
        try {
            conn = DriverManager.getConnection(url, username, passwd);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try{
        conn.setAutoCommit(false);
        PreparedStatement pstmt = conn.prepareStatement("insert into lztt (text) VALUES (?)") ;
            pstmt.execute("start TRANSACTION ");
            pstmt.setString(1,"memeda");
        pstmt.executeUpdate();
            PreparedStatement pstmt1 = conn.prepareStatement("insert into lztt (text) VALUES (?)") ;
            pstmt1.setString(1,"dsajdijsaoidjiosajdoisajdoijsaoidjsaoijdoisajidjsoajdijsaidjsajdsad");

            pstmt1.executeUpdate();
            conn.commit();
        }catch (Exception e){
            try {
                System.out.println(1);
                conn.rollback();
                System.out.println(2);
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            try {
                conn.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

通过上面的代码发现,是setautocommit(flase)不起作用,不起作用咋办,难道事务就这样没办法回滚了吗,这里我们可以手动的使用pstmt来开启事务这样就可以了pstmt.execute(“start TRANSACTION “);这是我们原生的使用方法,但是非原生的使用怎么办,也就是我们使用框架mybatis和spring。接下来介绍我的思路:

思路一

修改Spring的事务管理器,看过Spring源码的朋友都知道DataSourceTransactionManager是我们管理Spring和Mybatis事务的地方,doBegin()方法就是我们创建事务的地方,doBegin方法代码如下:

@Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;

        try {
            if (txObject.getConnectionHolder() == null ||
                    txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                Connection newCon = this.dataSource.getConnection();
                if (logger.isDebugEnabled()) {
                    logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
                }
                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
            }

            txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
            con = txObject.getConnectionHolder().getConnection();

            Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
            txObject.setPreviousIsolationLevel(previousIsolationLevel);

            // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
            // so we don't want to do it unnecessarily (for example if we've explicitly
            // configured the connection pool to set it already).
            if (con.getAutoCommit()) {
                txObject.setMustRestoreAutoCommit(true);
                if (logger.isDebugEnabled()) {
                    logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
                }
                con.setAutoCommit(false);
            }
            txObject.getConnectionHolder().setTransactionActive(true);

            int timeout = determineTimeout(definition);
            if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
            }

            // Bind the session holder to the thread.
            if (txObject.isNewConnectionHolder()) {
                TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
            }
        }

        catch (Throwable ex) {
            DataSourceUtils.releaseConnection(con, this.dataSource);
            throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
        }
    }

这里有一句话:con.setAutoCommit(false);但是对我们的中间件不起作用,这时我的思路来了,继承这个类然后重写doBegin()方法,然而我发现这是不可能的,有些对象的方法是protected我子类根本没办法使用,这里我放弃了。

思路二

所有代码重写,这工作量太大,考虑了一会就没想了

思路三

这也是我后面采取的思路,利用自定义注解加AOP的方法实现。我们可以利用AOP在方法执行之前就可以执行数据库的东西,这下问题来了,我怎么才能获得数据库连接了,看spring源码找了好久,找到了一个工具,利用数据库连接池可以获得绑定我们线程的链接。Connection connection = DataSourceUtils.getConnection(this.dataSource);
具体的切面:


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by lz on 2016/7/11.
 */
@Component
@Aspect
public class TransactionBeforeAop {
    private static final Logger log = LoggerFactory.getLogger(TransactionBeforeAop.class);
    @Autowired
    private DataSource dataSource;
    @Before("@annotation(com.bank.TransactionBefore)")
    public void before(JoinPoint joinPoint){
        Connection connection = DataSourceUtils.getConnection(this.dataSource);

        try {
            connection.prepareStatement("").execute("start TRANSACTION");
        } catch (SQLException e) {
            log.error("occur Exception",e.getMessage());
        }
    }
}

注解定义如下:


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by lz on 2016/7/11.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface TransactionBefore {

}

完成后我们就可以在Service中使用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值