繁体   English   中英

Spring 引导在一个事务中使用多个数据源

[英]Spring Boot use multiple DataSources in one Transaction

我的设置如下:

我有一个 Spring 启动 2.1.1 应用程序。 对于连接池,使用了 Hikari。

我想编写一项服务,在一个事务/方法中将 READ 路由到 READER 数据库,将 WRITES 路由到 Master 数据库。

所以我希望以下内容开始工作:

@Transactional
public test(Test test){
    Optional<Res> result = myRepo.findByAttr(test.getTest()); //do that on reader database
    validateIfResultExists(result); 

    myRepo.save(test);//do this on master database
}

为此,我编写了一个自定义 DbContextHolder:

public class DbContextHolder {
private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<DbType>();


public static void setDbType(DbType dbType) {
    if(dbType == null){
        throw new NullPointerException();
    }
    contextHolder.set(dbType);
}

public static DbType getDbType() {
    if(contextHolder.get() == null){
        DbContextHolder.setDbType(DbType.MASTER);
    }

    return (DbType) contextHolder.get();
}

public static void clearDbType() {
    contextHolder.remove();
}
}

我还有一个自定义的 RoutingDataSource:

public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
    return DbContextHolder.getDbType();
}
   }

还有一个用于使用 hikari 配置数据库的 DataSource:

@Configuration
public class DataSourceConfig {

private static final String PRIMARY_DATASOURCE_PREFIX = "spring.primary.datasource";
private static final String REPLICA_DATASOURCE_PREFIX = "spring.replica.datasource";

@Autowired
private Environment environment;

@Value("${spring.datasource.hikari.connectionTimeout}")
private Long CONNECTION_TIMEOUT;

@Value("${spring.datasource.hikari.idleTimeout}")
private Long IDLE_TIMEOUT;

@Value("${spring.datasource.hikari.maxLifetime}")
private Long MAX_LIFETIME_TIMEOUT;

@Value("${spring.datasource.hikari.minimumIdle}")
private int MINIMUM_IDLE;

@Value("${spring.datasource.hikari.maximumPoolSize}")
private int MAX_POOL_SIZE;

@Resource

@Bean
@Primary
public DataSource dataSource() {
    final RoutingDataSource routingDataSource = new RoutingDataSource();

    final DataSource primaryDataSource = buildDataSource("PrimaryHikariPool", PRIMARY_DATASOURCE_PREFIX);
    final DataSource replicaDataSource = buildDataSource("ReplicaHikariPool", REPLICA_DATASOURCE_PREFIX);

    final Map<Object, Object> targetDataSources = new HashMap<>();
    targetDataSources.put(DbType.MASTER, primaryDataSource);
    targetDataSources.put(DbType.REPLICA, replicaDataSource);

    routingDataSource.setTargetDataSources(targetDataSources);
    routingDataSource.setDefaultTargetDataSource(primaryDataSource);

    return routingDataSource;
}

private DataSource buildDataSource(String poolName, String dataSourcePrefix) {
    final HikariConfig hikariConfig = new HikariConfig();
    hikariConfig.setConnectionTimeout(CONNECTION_TIMEOUT);
    hikariConfig.setIdleTimeout(IDLE_TIMEOUT);
    hikariConfig.setMaxLifetime(MAX_LIFETIME_TIMEOUT);
    hikariConfig.setMinimumIdle(MINIMUM_IDLE);
    hikariConfig.setMaximumPoolSize(MAX_POOL_SIZE);

    hikariConfig.setPoolName(poolName);
    hikariConfig.setJdbcUrl(environment.getProperty(String.format("%s.url", dataSourcePrefix)));
    hikariConfig.setUsername(environment.getProperty(String.format("%s.username", dataSourcePrefix)));
    hikariConfig.setPassword(environment.getProperty(String.format("%s.password", dataSourcePrefix)));
    hikariConfig.setDriverClassName(environment.getProperty(String.format("%s.driver", dataSourcePrefix)));

    return new HikariDataSource(hikariConfig);
}

}

但这似乎不起作用。 如果我记录当前的 DbType,everythint 看起来很不错,但它不起作用,因为我收到以下错误:

错误:无法在只读事务中执行 INSERT

为了使事务分布式,您可以在事务周围添加方面(@Around("@annotation(transactional)")) ,并通过检查事务的类型,您可以应用必须调用的副本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM