多数据源配置(MyBatis-Plus vs AbstractRoutingDataSource)
·
MyBatis-Plus vs AbstractRoutingDataSource
MyBatis-Plus多数据源配
1.添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
2.配置数据源信息
spring:
datasource:
dynamic:
primary: master # 设置默认数据源
datasource:
master: # 主数据源
url: jdbc:mysql://localhost:3306/master_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave: # 从数据源
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
3.使用 @DS 注解切换数据源
在Service类或方法上使用 @DS(“数据源名称”) 指定数据源
@Service
@DS("master") // 类级别默认数据源
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@DS("slave") // 方法级别覆盖类级别数据源
public User getUserById(Long id) {
return userMapper.selectById(id);
}
}
4.排除原生自动配置(可选)
如果出现冲突,在启动类排除原生数据源自动配置:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
5.多数据源的事务一致性(@DSTransactional注解)
@DSTransactional
public void test() {
//db1数据源
DeAnOrganization deAnOrganization = new DeAnOrganization();
deAnOrganization.setId(1L);
deAnOrganizationMapper.insert(deAnOrganization);
//db2数据源
JdfLightBasic jdfLightBasic = new JdfLightBasic();
jdfLightBasic.setOrgId("1");
jdfLightBasicMapper.insert(jdfLightBasic);
//抛出异常,@DSTransactional可保证事务唯一性,事务可正常回滚
throw new RuntimeException("test");
}
AbstractRoutingDataSource
使用 AbstractRoutingDataSource 配置多数据源是 Spring 原生的动态数据源解决方案,适合需要深度定制的场景。
1.依赖仅需引入jdbc+连接池
<!-- 仅需 Spring JDBC + 连接池(使用druid) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
2.数据源配置
spring:
datasource:
master:
url: jdbc:mysql://localhost:3306/master_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
max-active: 20
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 3
max-active: 15
3.创建数据源上下文持有类
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void setDataSource(String dsName) {
CONTEXT.set(dsName);
}
public static String getDataSource() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
4.实现动态数据源路由
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
5.配置数据源Bean
@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DataSource dynamicDataSource(
@Qualifier("masterDataSource") DataSource master,
@Qualifier("slaveDataSource") DataSource slave) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", master);
targetDataSources.put("slave", slave);
DynamicDataSource ds = new DynamicDataSource();
ds.setDefaultTargetDataSource(master); // 默认数据源
ds.setTargetDataSources(targetDataSources);
ds.afterPropertiesSet();
return ds;
}
// 配置事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
6.自定义注解实现切换
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {
String value() default "master";
}
// 切面实现
@Aspect
@Component
public class DSAspect {
@Before("@annotation(ds)")
public void beforeSwitchDS(JoinPoint point, DS ds) {
DataSourceContextHolder.setDataSource(ds.value());
}
@After("@annotation(ds)")
public void afterSwitchDS(JoinPoint point, DS ds) {
DataSourceContextHolder.clear();
}
}
7.使用示例
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@DS("master") // 使用主库
public void addUser(User user) {
userMapper.insert(user);
}
@DS("slave") // 使用从库
public User getUser(Long id) {
return userMapper.selectById(id);
}
}
8.事务管理器配置
@Configuration
public class TransactionConfig {
// 主库事务管理器
@Bean
public PlatformTransactionManager masterTransactionManager(
@Qualifier("masterDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 从库事务管理器
@Bean
public PlatformTransactionManager slaveTransactionManager(
@Qualifier("slaveDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
// 自定义数据源切换注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {
String value() default "master";
}
// 数据源切换切面(需优先于事务切面执行)
@Aspect
@Component
@Order(0) // 优先级高于 @Transactional 的切面
public class DSAspect {
@Before("@annotation(ds)")
public void beforeSwitchDS(JoinPoint joinPoint, DS ds) {
DataSourceContextHolder.setDataSource(ds.value());
}
@After("@annotation(ds)")
public void afterSwitchDS(JoinPoint joinPoint, DS ds) {
DataSourceContextHolder.clear();
}
}
@Service
public class BusinessService {
// 显式指定事务管理器
@DS("master")
@Transactional(transactionManager = "masterTransactionManager")
public void createOrder(Order order) {
// 主库操作
}
@DS("slave")
@Transactional(transactionManager = "slaveTransactionManager")
public Order queryOrder(Long id) {
// 从库查询
}
// 跨数据源事务(需要分布式事务支持)
@DS("master")
@Transactional(transactionManager = "masterTransactionManager")
public void crossDataSourceOperation() {
// 主库操作
slaveOperation(); // 需要新事务
}
@DS("slave")
@Transactional(transactionManager = "slaveTransactionManager",
propagation = Propagation.REQUIRES_NEW)
public void slaveOperation() {
// 从库操作(独立事务)
}
}
关键注意事项
AOP 执行顺序
必须确保数据源切换切面(@DS)先于事务切面执行:
// 启动类配置
@SpringBootApplication
@EnableTransactionManagement(order = Ordered.LOWEST_PRECEDENCE) // 事务切面最后执行
public class Application { ... }
事务传播机制
默认 Propagation.REQUIRED 会继承当前事务的数据源
使用 Propagation.REQUIRES_NEW 可强制开启新事务并切换数据源
分布式事务支持
如需跨数据源事务原子性,需集成 Seata 等分布式事务框架:
@DS("master")
@GlobalTransactional // Seata 全局事务注解
public void distributedTransaction() {
// 操作多个数据源
}
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)