【spring】spring boot多数据源配置(方式二)

上篇文章讲述了一种简单粗暴的多数据源配置方式,这篇来讲一下动态切换数据源的方式配置。

动态切换数据源,其核心在于一个AbstractRoutingDataSource类,通过继承此类并重写determineCurrentLookupKey方法可以实现动态切换数据源,具体切换方式可点进去看源码的determineTargetDataSource方法,比较简单,这里只记录实现。

数据库准备
参考上篇文章:http://www.scarlettbai.com/index.php/archives/112.html

动态数据源配置

public class MyDynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.get();
    }
}

这个方法的返回值,就是一个数据源的key,我们对于多个数据源,会对每个数据源定义一个key,之后以map形式保存在AbstractRoutingDataSource的targetDataSources属性中。

接下来看一下上文中出现的DynamicDataSourceContextHolder类:

public class DynamicDataSourceContextHolder {

    private static ThreadLocal<String> DBNAME = new ThreadLocal<>();

    public static String get() {
        return DBNAME.get();
    }

    public static void set(String dbName) {
        DBNAME.set(dbName);
    }

    public static void clear() {
        DBNAME.remove();
    }
}

DynamicDataSourceContextHolder类的作用很简单,就是根据线程来存取数据库的key。

DataSource配置

@Configuration
public class DataSourceConfig {

    @Autowired
    private Environment env;

    @Bean
    public DataSource backDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(env.getProperty("spring.datasource.back.jdbcUrl"));
        dataSource.setUsername(env.getProperty("spring.datasource.back.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.back.password"));
        return dataSource;
    }

    @Bean
    public DataSource frontDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(env.getProperty("spring.datasource.front.jdbcUrl"));
        dataSource.setUsername(env.getProperty("spring.datasource.front.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.front.password"));
        return dataSource;
    }

    @Bean
    @Primary
    public DataSource dynamicDataSource() {
        MyDynamicDataSource dataSource = new MyDynamicDataSource();
        dataSource.setDefaultTargetDataSource(frontDataSource());

        Map<Object, Object> allDataSource = new HashMap<>();
        allDataSource.put("backDataSource", backDataSource());
        allDataSource.put("frontDataSource", frontDataSource());
        dataSource.setTargetDataSources(allDataSource);
        return dataSource;
    }

}

这里datasource配置引入了阿里的druid数据源,各位不用的话可以切换为自己的数据源即可,主要注意一下dynamicDataSource这个数据源,这里需要将他设置为主数据源,即@Primary。之后调用数据源都会走determineTargetDataSource进行数据源路由切换。

在调用dao前进行数据源切换

这里通过AOP方式实现,看各人系统需求,可以直接通过aop扫描对应的分包目录,也可以自定义一个注解来自己在方法上添加注解。

//定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DynamicDataSource {

    /**
     * dbname
     * @return
     */
    String value();
}

//AOP实现
@Aspect
@Component
public class DynamicDataSourceAOP {

    @Before("@annotation(org.white.mutidatasource2.aop.DynamicDataSource)")
    public void changeDataSource(JoinPoint point) {
        Class<?> clazz = point.getTarget().getClass();
        MethodSignature signature = (MethodSignature) point.getSignature();
        try {
            Method method = clazz.getDeclaredMethod(signature.getName(), signature.getParameterTypes());
            if (method.isAnnotationPresent(DynamicDataSource.class)) {
                DynamicDataSource dynamicDataSource = method.getAnnotation(DynamicDataSource.class);
                String dbName = dynamicDataSource.value();
                if (DynamicDataSourceContextAware.names.contains(dbName)) {
                    DynamicDataSourceContextHolder.set(dbName);
                }
            }
        } catch (Exception e) {
            System.out.println("error" + e.getMessage());
        }
    }

    @After("@annotation(org.white.mutidatasource2.aop.DynamicDataSource)")
    public void after() {
        DynamicDataSourceContextHolder.clear();
    }

}

这里的aop实现也很简单,就是通过读取注解内容来切换DynamicDataSourceContextHolder中的值。

其中的DynamicDataSourceContextAware类代码如下,只是记录一个names的集合,可以看需求省略:

@Component
public class DynamicDataSourceContextAware implements ApplicationContextAware {

    public static Set<String> names = new HashSet<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, DataSource> dataSources = applicationContext.getBeansOfType(DataSource.class);
        for (String dataSourceName : dataSources.keySet()) {
            if (!"dynamicDataSource".equals(dataSourceName)) {
                names.add(dataSourceName);
            }
        }
    }
}

最后,来看一下调用方式:

@Service
public class BizServiceImpl implements BizService {

    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private UserMapper userMapper;

    //无注解,使用默认datasource
    @Override
    public int addUser(UserDTO userDTO) {
        return userMapper.insert(userDTO);
    }

    //使用backDatasource
    @Override
    @DynamicDataSource(value = "backDataSource")
    public int addAdmin(AdminDTO adminDTO) {
        return adminMapper.insert(adminDTO);
    }
}

实现过程就在这里了,其核心其实就是一个AbstractRoutingDataSource类,其他都是一些spring的基础知识。

这种模式优点在于扩展方便,新增数据源只需新增一个datasource的配置即可。

另外这里附上github项目地址:https://github.com/whiteBX/mutidatasource2

可以直接下载运行,包括初始化sql都在项目内。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值