spring事务和mybatis是如何使用同一个连接

2022-07-28  本文已影响0人  念䋛

我们知道事务是针对同一个connection来说的,先将connection自动提交设置为false,再和数据库交互结束后commit提交事务,spring中提供了简便的注解方式实现事务,事务的源码可以找到connection的自动提交设置为false,但是mybatis真正执行sql语句的时候,如何保证和事务的连接是同一个connection的,如果不是同一个,则不能保证事务.
事务源码
org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction


image.png

org.springframework.transaction.support.TransactionSynchronizationManager#bindResource
--org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
开始事务的时候会创建connection,将connection自动提交false,后续的bindResource方法中将ConnectionHolder存放到ThreadLocal中


image.png

将ConnectionHolder对象,放到TransactionSynchronizationManager的resources成员变量中
org.springframework.transaction.support.TransactionSynchronizationManager#bindResource

    public static void bindResource(Object key, Object value) throws IllegalStateException {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Assert.notNull(value, "Value must not be null");
        Map<Object, Object> map = resources.get();
        // set ThreadLocal Map if none found
        if (map == null) {
            map = new HashMap<>();
            resources.set(map);
        }
//存储ConnectionHolder
        Object oldValue = map.put(actualKey, value);
        // Transparently suppress a ResourceHolder that was marked as void...
        if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
            oldValue = null;
        }
        if (oldValue != null) {
            throw new IllegalStateException(
                    "Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");
        }
    }

在TransactionSynchronizationManager#doGetResource方法获取ConnectionHolder

    private static Object doGetResource(Object actualKey) {
//private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");
//resources是ThreadLocal
        Map<Object, Object> map = resources.get();
        if (map == null) {
            return null;
        }
        Object value = map.get(actualKey);
        // Transparently remove ResourceHolder that was marked as void...
        if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
            map.remove(actualKey);
            // Remove entire ThreadLocal if empty...
            if (map.isEmpty()) {
                resources.remove();
            }
            value = null;
        }
        return value;
    }

执行mybatis执行sql的时候是如何调用TransactionSynchronizationManager#doGetResource方法,从TheadLocal中获取ConnectionHolder的


image.png

这里不讲解mybatis的源码,贴出来调用链,可以看到如何从ThreadLocal获取到了ConnectionHolder的
分析到这可知,从开启事务到sql的执行需要同一个线程,如果执行sql的代码放在多线程中,该sql不能保证在事务里.

上一篇下一篇

猜你喜欢

热点阅读