Spring Boot多数据源下自定义@ReadReplica注解切换MyBatis数据源咨询
这个需求完全可以实现。你当前配置的是两套独立的MyBatis运行环境,仅支持按Mapper包路径分配数据源,无法实现方法级别的切换,需要调整为动态路由数据源的方案,具体实现步骤如下:
实现步骤
第一步:创建数据源上下文持有类
用ThreadLocal存储当前线程使用的数据源标识,避免线程间互相干扰:
public class DataSourceContextHolder { private static final ThreadLocal<String> DATASOURCE_KEY = new ThreadLocal<>(); public static void setKey(String key) { DATASOURCE_KEY.set(key); } public static String getKey() { return DATASOURCE_KEY.get(); } public static void clear() { DATASOURCE_KEY.remove(); } }
第二步:创建动态路由数据源
继承Spring提供的AbstractRoutingDataSource实现动态数据源路由逻辑:
public class DynamicRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getKey(); } }
第三步:替换原有的多数据源配置
删除你现有PrimaryMyBatisConfig和ReadReplicaMyBatisConfig两个配置类,使用统一的动态数据源配置:
@Configuration @Slf4j public class DynamicDataSourceConfig { @Value("${mybatis.mapper-locations}") private String mapperLocations; // 主数据源 @Bean(name = "primaryDataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } // 读副本数据源 @Bean(name = "readReplicaDataSource") @ConfigurationProperties(prefix = "spring.datasource-read-replica") public DataSource readReplicaDataSource() { return DataSourceBuilder.create().build(); } // 动态路由数据源作为默认数据源 @Primary @Bean(name = "dynamicDataSource") public DataSource dynamicDataSource(@Qualifier("primaryDataSource") DataSource primary, @Qualifier("readReplicaDataSource") DataSource readReplica) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("primary", primary); targetDataSources.put("readReplica", readReplica); DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource(); routingDataSource.setTargetDataSources(targetDataSources); // 默认使用主数据源 routingDataSource.setDefaultTargetDataSource(primary); return routingDataSource; } @Bean public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dynamicDataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:" + mapperLocations)); return bean.getObject(); } @Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } @Bean @Primary public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) { return new DataSourceTransactionManager(dynamicDataSource); } }
第四步:编写AOP切面实现注解自动切换数据源
@Aspect @Component public class ReadReplicaAspect { // 切点修改为你自己项目Mapper接口所在的包路径 @Pointcut("execution(* com.yourproject.mapper.*.*(..))") public void mapperPointcut() {} @Around("mapperPointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 方法标注@ReadReplica则切换到读副本数据源 if (method.isAnnotationPresent(ReadReplica.class)) { DataSourceContextHolder.setKey("readReplica"); } try { return joinPoint.proceed(); } finally { // 执行完后清除上下文,避免线程复用导致数据源串用 DataSourceContextHolder.clear(); } } }
最后在Spring Boot启动类上添加@EnableAspectJAutoProxy注解开启AOP功能即可。
注意事项
如果存在事务嵌套场景,需要注意:如果外层方法已经开启了主数据源的事务,内层标注@ReadReplica的方法默认不会切换数据源,因为事务会绑定固定的数据库连接。如果需要在事务内切换数据源,需要将读方法的事务传播级别设置为REQUIRES_NEW,但会带来事务拆分的副作用,建议读请求尽量不要包裹在写事务内。
内容的提问来源于stack exchange,提问作者steelcityamir
相关产品推荐
相关产品推荐

