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_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave: # 从数据源url: jdbc:mysql://localhost:3306/slave_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
3.使用 @DS 注解切换数据源
在Service类或方法上使用 @DS(“数据源名称”) 指定数据源
@Service
@DS("master") // 类级别默认数据源
public class UserServiceImpl implements UserService {@Autowiredprivate 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注解)
@DSTransactionalpublic 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_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5max-active: 20slave:url: jdbc:mysql://localhost:3306/slave_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 3max-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 {@Overrideprotected 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@Primarypublic 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;}// 配置事务管理器@Beanpublic 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 {@Autowiredprivate 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 {// 主库事务管理器@Beanpublic PlatformTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}// 从库事务管理器@Beanpublic 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() {// 操作多个数据源
}