mybatis-plus @DS 注解内部方法调用导致的数据源切换问题

在同一个类的不同方法中使用 @DS 注解来切换数据源时,需要注意一些潜在的问题。具体来说,如果一个方法调用另一个方法,并且这两个方法上都有不同的 @DS 注解,那么数据源的切换可能不会按预期工作。这是因为注解通常通过 AOP(面向切面编程)机制来实现,在方法调用链中,内部方法的数据源切换可能不会生效。

为了更好地理解这个问题,我们可以通过一个具体的例子来说明。假设你有一个服务类 YourService,其中有两个方法 listlistDict,分别使用 @DS("slave")@DS("master") 注解:

import java.util.List;

public class YourService {

    private SlaveMapper slaveMapper;
    private DictMapper dictMapper;

    // 构造函数或者通过依赖注入初始化 mapper
    public YourService(SlaveMapper slaveMapper, DictMapper dictMapper) {
        this.slaveMapper = slaveMapper;
        this.dictMapper = dictMapper;
    }

    @DS("slave")
    public List<KeyValueVo> list(String areaCode) {
        List<KeyValueVo> list = slaveMapper.list(areaCode);
        List<Dict> educations = listDict("education");
        // ...后续操作...
        return list; // 假设需要返回这个列表
    }

    @DS("master")
    public List<Dict> listDict(String dictType) {
        List<Dict> list = dictMapper.list(dictType);
        return list;
    }
}

在这个例子中,当 list 方法调用 listDict 方法时,listDict 方法上的 @DS("master") 注解可能不会生效。原因在于,AOP 代理机制通常只会在外部调用时生效,而内部方法调用不会触发 AOP 切面。

解决方案

方案一:提取公共逻辑到单独的服务类

listDict 方法移到一个新的服务类中,这样可以确保每个方法都可以被独立地拦截和处理数据源切换。

@Service
public class DictService {

    private DictMapper dictMapper;

    // 构造函数或者通过依赖注入初始化 mapper
    public DictService(DictMapper dictMapper) {
        this.dictMapper = dictMapper;
    }

    @DS("master")
    public List<Dict> listDict(String dictType) {
        List<Dict> list = dictMapper.list(dictType);
        return list;
    }
}

@Service
public class YourService {

    private SlaveMapper slaveMapper;
    private DictService dictService;

    // 构造函数或者通过依赖注入初始化 mapper 和 service
    public YourService(SlaveMapper slaveMapper, DictService dictService) {
        this.slaveMapper = slaveMapper;
        this.dictService = dictService;
    }

    @DS("slave")
    public List<KeyValueVo> list(String areaCode) {
        List<KeyValueVo> list = slaveMapper.list(areaCode);
        List<Dict> educations = dictService.listDict("education");
        // ...后续操作...
        return list; // 假设需要返回这个列表
    }
}
方案二:手动设置数据源

如果你不想将 listDict 方法移到新的服务类中,可以在 list 方法中手动设置数据源。不过这种方法比较复杂,通常不推荐。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    public static void clearDataSourceKey() {
        contextHolder.remove();
    }
}

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceKey();
    }
}

@Service
public class YourService {

    private SlaveMapper slaveMapper;
    private DictMapper dictMapper;

    // 构造函数或者通过依赖注入初始化 mapper
    public YourService(SlaveMapper slaveMapper, DictMapper dictMapper) {
        this.slaveMapper = slaveMapper;
        this.dictMapper = dictMapper;
    }

    @DS("slave")
    public List<KeyValueVo> list(String areaCode) {
        List<KeyValueVo> list = slaveMapper.list(areaCode);
        try {
            DataSourceContextHolder.setDataSourceKey("master");
            List<Dict> educations = dictMapper.list("education");
            // ...后续操作...
        } finally {
            DataSourceContextHolder.clearDataSourceKey();
        }
        return list; // 假设需要返回这个列表
    }
}

总结

最简单和推荐的方法是将不同数据源的操作放在不同的服务类中,这样可以确保每个方法都能被独立地拦截和处理数据源切换。这种方式不仅符合面向对象的设计原则,还能避免复杂的内部方法调用导致的数据源切换问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学亮编程手记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值