@DS(“#header.dbTenantId”) 是 MyBatis-Plus 框架中的注解,用于指定数据源。其中 #header.dbTenantId 表示从请求头中获取 dbTenantId 参数的值,作为数据源的标识。
@DS(“#tenantId”)中的 #tenantId 表示从方法参数中获取 tenantId 参数的值,作为数据源的标识。
@DS(“master”) 中的 “master” 表示数据源的标识,即指定使用名为 master 的数据源。
在多租户系统中,不同的租户可能需要连接不同的数据库,因此需要动态切换数据源。使用 @DS 注解可以方便地实现动态切换数据源的功能。通过在注解中指定数据源的标识,可以让 MyBatis-Plus 框架自动切换到对应的数据源,从而实现动态切换数据源的功能。
需要注意的是,使用 @DS 注解需要在 Spring Boot 配置文件中配置多个数据源,并在注解中指定数据源的标识。同时,也需要在代码中使用 @Mapper 注解标注 Mapper 接口,以便 MyBatis-Plus 框架能够自动扫描并生成对应的 Mapper 实现类。
样例:
spring:
datasource:
tenant1:
url: jdbc:mysql://localhost:3306/tenant1_db?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
tenant2:
url: jdbc:mysql://localhost:3306/tenant2_db?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
在上面的配置文件中,定义了两个数据源,分别为 tenant1 和 tenant2。其中,tenant1 数据源连接的是 tenant1_db 数据库,tenant2 数据源连接的是 tenant2_db 数据库。
在代码中,可以使用 @DS 注解动态指定使用哪个租户的数据源。例如:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@DS("#tenantId")
@Override
public List<User> listUsers(Long tenantId) {
return userMapper.selectList(null);
}
}
失效情况:
updateUser方法的数据源会覆盖掉getUserById方法的数据源
可以将getUserById方法放到另一个类中,使之代理注解生效
@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("dataSource1")
private DataSource dataSource1;
@Autowired
@Qualifier("dataSource2")
private DataSource dataSource2;
@Override
@DS("dataSource1")
@Transactional(value = "transactionManager1")
public User getUserById(int id) {
// 使用dataSource1查询用户信息
// ...
}
@Override
@Transactional(value = "transactionManager2")
@DS("dataSource2")
public void updateUser(User user) {
// 在这里调用getUserById方法,应该使用dataSource1数据源
User oldUser = getUserById(user.getId());
// 使用dataSource2更新用户信息
// ...
}
}
原因:
@DS注解是通过AOP实现的,它会在方法执行前切换数据源。而@Transactional注解也是通过AOP实现的,它会在方法执行前开启事务,并在方法执行后提交或回滚事务。
当一个方法同时被@DS和@Transactional注解修饰时,Spring会先创建一个代理对象,这个代理对象会同时包含@DS和@Transactional的功能。当代理对象调用这个方法时,它会先切换数据源,然后开启事务。在事务执行期间,如果这个方法调用了另一个方法,那么这个方法也会被代理对象所代理,也就是说,这个方法也会被切换到当前数据源,并且也会被包含在当前事务中。
如果在调用另一个方法时,这个方法上也有@DS注解,那么这个注解会被代理对象所覆盖,也就是说,这个方法会使用当前数据源,而不是它本来应该使用的数据源。这就是为什么@DS注解会被覆盖的原因。