若依数据源切换失效关键解决,显示切换了数据源,其实使用的还是主数据源

若依分离版 Oracle 主从数据源配置及手动切换失败解决办法

前言

在使用若依框架进行多数据源开发时,经常会遇到需要操作多个数据库进行 CRUD 操作的需求。然而,在配置完成后可能会发现数据源切换不生效的问题。经过深入排查发现,这个问题通常与 @Transactional 注解的使用有关。

一、数据源配置

1. 配置从库数据源

application-druid.yml 文件中添加从库数据源配置:

# 从库数据源
slave:
	# 从数据源开关/默认关闭
	enabled: true
	url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
	username: root
	password: password

2. DruidConfig 配置

DruidConfig 类中配置读取和添加数据源:

@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties) {
	DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
	return druidProperties.dataSource(dataSource);
}

@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource) {
	Map<Object, Object> targetDataSources = new HashMap<>();
	targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
	// 添加从库数据源
	setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
	return new DynamicDataSource(masterDataSource, targetDataSources);
}

二、使用方法

1. 注解方式切换数据源

方法级别使用

在需要使用多数据源的方法上添加 @DataSource 注解:

@DataSource(value = DataSourceType.SLAVE)
public List<SysUser> selectUserList(SysUser user) {
	return userMapper.selectUserList(user);
}
类级别使用

在整个 Service 类上添加注解,该类的所有方法都会使用指定数据源:

@Service
@DataSource(value = DataSourceType.SLAVE)
public class SysUserServiceImpl {
	// 类中所有方法都使用从库
}

2. 手动切换数据源

通过编程方式手动切换数据源:

public List<SysUser> selectUserList(SysUser user) {
	// 手动设置数据源
	DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
	
	try {
		List<SysUser> userList = userMapper.selectUserList(user);
		return userList;
	} finally {
		// 清除数据源设置
		DynamicDataSourceContextHolder.clearDataSourceType();
	}
}

三、切换不生效问题分析与解决

问题现象

使用手动切换数据源的方法时,代码运行过程中始终操作的是同一个数据库,数据源切换不生效。

问题根因

经过排查发现,问题主要是由于添加了 @Transactional(rollbackFor = Exception.class) 注解导致的。这与 Spring 的事务管理机制密切相关:

1. 事务范围内的数据源切换限制

当方法被 @Transactional 注解标记时,Spring 会为该方法创建一个事务上下文。如果事务管理器配置为在同一个事务范围内使用相同的数据源,则后续的数据源切换操作可能会被忽略。

2. 默认事务管理器的配置限制

Spring 默认的事务管理器(如 DataSourceTransactionManager)可能会限制数据源切换的作用范围,导致事务管理器忽略在方法中手动设置的数据源切换操作。

3. 事务代理的影响

使用 @Transactional 注解时,Spring 会在运行时动态创建代理对象来管理事务。这可能导致在方法调用时,数据源切换操作被代理对象拦截或忽略。

解决方案

方案一:避免使用 @Transactional 注解
// 不使用 @Transactional 注解,手动管理事务
public List<SysUser> selectUserList(SysUser user) {
	DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
	try {
		List<SysUser> userList = userMapper.selectUserList(user);
		return userList;
	} finally {
		DynamicDataSourceContextHolder.clearDataSourceType();
	}
}
方案二:使用 AspectJ 切面管理事务

考虑使用 AspectJ 切面来管理事务,以绕过 Spring 默认的代理机制:

@Aspect
@Component
public class DataSourceTransactionAspect {
	
	@Around("@annotation(dataSource)")
	public Object around(ProceedingJoinPoint point, DataSource dataSource) throws Throwable {
		String dataSourceKey = dataSource.value().name();
		DynamicDataSourceContextHolder.setDataSourceType(dataSourceKey);
		
		try {
			return point.proceed();
		} finally {
			DynamicDataSourceContextHolder.clearDataSourceType();
		}
	}
}
方案三:优化事务管理器和数据源配置

检查并优化事务管理器和数据源配置,确保它们与数据源切换需求一致:

@Configuration
public class TransactionConfig {
	
	@Bean
	public PlatformTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
		return new DataSourceTransactionManager(dynamicDataSource);
	}
}

四、最佳实践建议

1. 数据源切换原则

  • 读写分离:查询操作使用从库,写入操作使用主库
  • 业务隔离:不同业务模块使用不同数据源
  • 异常处理:确保数据源切换后能够正确清理

2. 注意事项

  • 在使用手动切换时,必须在 finally 块中清理数据源设置
  • 避免在同一个事务中切换多个数据源
  • 合理设计数据源切换的颗粒度,避免频繁切换

3. 调试技巧

// 添加日志输出,便于调试数据源切换情况
public List<SysUser> selectUserList(SysUser user) {
	String currentDataSource = DynamicDataSourceContextHolder.getDataSourceType();
	log.info("Current DataSource: {}", currentDataSource);
	
	DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
	log.info("Switched to DataSource: {}", DataSourceType.SLAVE.name());
	
	try {
		return userMapper.selectUserList(user);
	} finally {
		DynamicDataSourceContextHolder.clearDataSourceType();
		log.info("DataSource cleared");
	}
}

总结

若依框架的多数据源切换功能强大且灵活,但在实际使用中需要注意事务管理对数据源切换的影响。通过合理的配置和正确的使用方式,可以有效解决数据源切换不生效的问题,实现稳定可靠的多数据源操作。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值