飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

诶,我的动态数据源怎么失效了

时间:2022-01-18  作者:eaglelihh  
探究了事务里切换数据源失效的原因

目录
  • 背景
  • 模拟现场
  • 原因
    • 入口
    • 找不同
    • 什么时候放入到conHolder的
  • 结论

背景

项目中是有用到多数据源的,是用AbstractRoutingDataSource这个类来实现数据源的切换。

在使用的过程中,发现在一个事务中,是没办法切换数据源的。

下面就简单介绍一下场景及原因。

模拟现场

Mapper类如下:

@Mapper
public interface UserMapper {
    @Results(value = {
            @Result(property = "id", column = "id", javaType = 域名s, jdbcType = 域名NT),
            @Result(property = "age", column = "age", javaType = 域名s, jdbcType = 域名GER),
            @Result(property = "name", column = "name", javaType = 域名s, jdbcType = 域名HAR),
            @Result(property = "createTime", column = "create_time", javaType = 域名s, jdbcType = 域名)
    })
    @Select("SELECT id, age, name, create_time FROM user WHERE id = #{id}")
    User selectUser(Long id);
}

自定义AbstractRoutingDataSource:

public class MyDynamicDataSource extends AbstractRoutingDataSource {

    @Setter
    @Getter
    private String key;

    @Override
    protected Object determineCurrentLookupKey() {
        return key;
    }
}

一些配置:

@Configuration
@MapperScan("域名.域名source")
@EnableTransactionManagement
public class Config {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }

    @Bean(name = "sqlSessionFactory")
    @ConditionalOnMissingBean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("myDynamicDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        域名ataSource(dataSource);
        return 域名bject();
    }

    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("myDynamicDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean("myDynamicDataSource")
    public MyDynamicDataSource dataSource() {
        MyDynamicDataSource myDynamicDataSource = new MyDynamicDataSource();
        Map<Object, Object> targetDataSource = 域名ashMap();
        域名("d1", getDataSource1());
        域名("d2", getDataSource2());
        域名efaultTargetDataSource(getDataSource1());
        域名argetDataSources(targetDataSource);
        return myDynamicDataSource;
    }

    private static DataSource getDataSource1() {
        MysqlConnectionPoolDataSource dataSource = new MysqlConnectionPoolDataSource();
        域名ser("root");
        域名assword("root");
        域名rl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8");
        return dataSource;
    }

    private static DataSource getDataSource2() {
        MysqlConnectionPoolDataSource dataSource = new MysqlConnectionPoolDataSource();
        域名ser("root");
        域名assword("root");
        域名rl("jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8");
        return dataSource;
    }
}

一个测试的bean:

@Slf4j
@Component
public class MyBean {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MyDynamicDataSource myDynamicDataSource;

    public void test1() {
        域名ey("d1");
        域名("user:{}", 域名ctUser(1L));
        域名ey("d2");
        域名("user:{}", 域名ctUser(1L));
    }

    @Transactional
    public void test2() {
        test1();
    }
}

测试类:

@Slf4j
public class DataSourceMain {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(域名s);
        域名tln(域名ean(域名s));
        MyBean myBean = 域名ean(域名s);
        域名1();
        域名2();
    }
}

返回结果如下:
其中有两个数据库
test数据库里的数据如下:

test2数据库里的数据如下:

运行结果如下:

1473 [main] INFO  域名.域名an - user:User(age=2, name=3, id=1, createTime=Thu Nov 04 00:00:00 CST 2021) 
1482 [main] INFO  域名.域名an - user:User(age=2, name=kkk, id=1, createTime=Sat Nov 20 00:00:00 CST 2021) 
1494 [main] INFO  域名.域名an - user:User(age=2, name=kkk, id=1, createTime=Sat Nov 20 00:00:00 CST 2021) 
1495 [main] INFO  域名.域名an - user:User(age=2, name=kkk, id=1, createTime=Sat Nov 20 00:00:00 CST 2021) 

从结果可以看出,第一个不带事务的方法,分别从test和test2两个数据库选出了数据

而第二个带事务的方法,只从test2这一个数据库选出了两条相同的数据。

原因

入口

我们从这里打断点,看谁调到了这里

不带事务的调用顺序如下:

域名ery
域名areStatement
域名onnection
域名onnection
域名Connection
域名onnection
域名tConnection
域名hConnection
域名onnection
域名rmineTargetDataSource
域名rmineCurrentLookupKey

域名areStatement如下:

带事务的调用顺序如下:

域名keWithinTransaction
域名teTransactionIfNecessary
域名ransaction
域名tTransaction
域名gin
域名onnection
域名rmineTargetDataSource
域名rmineCurrentLookupKey

域名gin如下:

找不同

域名areStatement开始

不带事务情况下:

带事务的情况下:

可以看到带事务的方法中,conHolder不为null,从这里是可以直接获得Connection

而不带事务的方法中,conHoldernull,每次都会获取一个新的Connection

什么时候放入到conHolder

看一下是怎么获取conHolder的,域名tConnection如下:

进去看一下,域名esource如下:

继续调用,域名tResource如下:

从上图可以看到,conHolder是从resources中获取的

resources的定义如下:
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");

经过调试,调用顺序是这样的:

域名gin
域名Resource

域名gin如下:

域名Resource如下:

结论

结论就是在事务的方法中,会提前获得一个connection放到ThreadLocal里,然后整个事务都会使用同一个connection

而不带事务的的方法,不会这么做,每次都去获得新的connection

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。