多数据源( 数据源 进行切换)
AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey() 决定使用哪个数据源。
1、application.yml中配置多个数据源
# Order
spring.datasource.order.url =jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.order.username =root
spring.datasource.order.password =123456
spring.datasource.order.driver-class-name =com.mysql.cj.jdbc.Driver
# Storage
spring.datasource.storage.url =jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.storage.username =root
spring.datasource.storage.password =123456
spring.datasource.storage.driver-class-name =com.mysql.cj.jdbc.Driver
# Pay
spring.datasource.pay.url =jdbc:mysql://localhost:3306/seata_pay?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.pay.username =root
spring.datasource.pay.password =123456
spring.datasource.pay.driver-class-name =com.mysql.cj.jdbc.Driver
2、主启动类添加注解
@SpringBootApplication
@MapperScan("com.example.demo.mapper" )
3、编写配置类
@Getter
public enum DataSourceKey {
/**
* Order data source key.
*/
ORDER ,
/**
* Storage data source key.
*/
STORAGE ,
/**
* Pay data source key.
*/
PAY ,
}
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial (DataSourceKey.ORDER ::name);
private static List<Object> dataSourceKeys = new ArrayList<>();
public static void setDataSourceKey(DataSourceKey key) {
CONTEXT_HOLDER .set(key.name());
}
public static String getDataSourceKey() {
return CONTEXT_HOLDER .get();
}
public static void clearDataSourceKey() {
CONTEXT_HOLDER .remove();
}
public static List<Object> getDataSourceKeys() {
return dataSourceKeys ;
}
}
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
//log.info("当前数据源 [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey ();
}
}
@Configuration
public class DataSourceProxyConfig {
@Bean("originOrder" )
@ConfigurationProperties(prefix = "spring.datasource.order" )
public DataSource dataSourceMaster() {
return new DruidDataSource();
}
@Bean("originStorage" )
@ConfigurationProperties(prefix = "spring.datasource.storage" )
public DataSource dataSourceStorage() {
return new DruidDataSource();
}
@Bean("originPay" )
@ConfigurationProperties(prefix = "spring.datasource.pay" )
public DataSource dataSourcePay() {
return new DruidDataSource();
}
@Bean(name = "order" )
public DataSourceProxy masterDataSourceProxy(@Qualifier("originOrder" ) DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean(name = "storage" )
public DataSourceProxy storageDataSourceProxy(@Qualifier("originStorage" ) DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean(name = "pay" )
public DataSourceProxy payDataSourceProxy(@Qualifier("originPay" ) DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean("dynamicDataSource" )
public DataSource dynamicDataSource(@Qualifier("order" ) DataSource dataSourceOrder,
@Qualifier("storage" ) DataSource dataSourceStorage,
@Qualifier("pay" ) DataSource dataSourcePay) {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(3);
dataSourceMap.put(DataSourceKey.ORDER .name(), dataSourceOrder);
dataSourceMap.put(DataSourceKey.STORAGE .name(), dataSourceStorage);
dataSourceMap.put(DataSourceKey.PAY .name(), dataSourcePay);
dynamicRoutingDataSource.setDefaultTargetDataSource(dataSourceOrder);
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
DynamicDataSourceContextHolder.getDataSourceKeys ().addAll(dataSourceMap.keySet());
return dynamicRoutingDataSource;
}
@Bean
@ConfigurationProperties(prefix = "mybatis-plus" ) // MybatisSqlSessionFactoryBean中有各种MybatisPlus的配置属性(globalConfig、mapper L ocations} 而SqlSessionFactoryBean中则是mybatis的各种配置属性(typeAlies 、mapper L ocations )
public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dynamicDataSource" ) DataSource dataSource) {
// 这里用 MybatisSqlSessionFactoryBean 代替了 SqlSessionFactoryBean,否则 MyBatisPlus 不会生效
MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
mybatisSqlSessionFactoryBean.setDataSource(dataSource);
return mybatisSqlSessionFactoryBean;
}
}
调用切换数据源:
@GlobalTransactional
@Override
public OperationResponse placeOrder(PlaceOrderRequestVO placeOrderRequestVO) throws Exception {
DynamicDataSourceContextHolder. setDataSourceKey (DataSourceKey. ORDER ); //切换数据源
Integer amount = 1;
Integer price = placeOrderRequestVO.getPrice();
Order order = Order.builder ().build();
Integer saveOrderRecord = orderDao .insert(order);
// 扣减库存
boolean operationStorageResult = storageService .reduceStock(placeOrderRequestVO.getProductId(), amount);
// 扣减余额
boolean operationBalanceResult = payService .reduceBalance(placeOrderRequestVO.getUserId(), price);
DynamicDataSourceContextHolder. setDataSourceKey (DataSourceKey. ORDER ); //切换数据源
order.setStatus(OrderStatus.SUCCESS );
Integer updateOrderRecord = orderDao .updateById(order);
return success(operationStorageResult && operationBalanceResult);
}
项目启动报错: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured
原因:导入spring-mybatis依赖后,springboot启动时会自动加载数据源,由于dataSource配置成多数据源加载不到spring.datasource.url故而报错。
解决:1、主启动类添加@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
2、若上面配置还是无法解决,可以配置一个默认数据源让其启动时加载(不影响,会被多数据源切换时覆盖的):spring.datasource.url
额外:
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession=factory.openSession(); //sqlSession就是用来操作sql语句的
使用 MyBatis-Spring 之后, 会使用SqlSessionFactoryBean来代替SqlSessionFactoryBuilder创建SqlSessionFactory
MybatisPlus需要使用MybatisSqlSessionFactoryBean。