mybatis多数据源动态切换的完整步骤

网友投稿 278 2023-02-20

mybatis多数据源动态切换的完整步骤

笔者主要从事c#开发,近期因为项目需要,搭建了一套spring-cloud微服务框架,集成了eureka服务注册中心、

gateway网关过滤、admin服务监控、auth授权体系验证,集成了redis、swagger、jwt、mybatis多数据源等各项功能。

具体搭建过程后续另写播客介绍。具体结构如下:

在搭建过程集成mybatis的时候,考虑到单一数据源无法满足实际业务需要,故结合c#的开发经验,进行多数据源动态集成。

mybatis的多数据源可以采用两种方式进行,第一种是分包方式实现,这种方式灵活性不高,而且较为繁琐,故不做过多介绍。

另一种方式是采用AOP的思想,进行注解动态切换,参考网上教程,核心思想是依靠 继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法,在该方法中使用DatabaseContextHolder获取当前线程的dataSource。

但是网上方法大都是首先定义好各个datasource,比如有三个数据源,就需要实现定义好三个datasource,笔者感觉这种方法,在我目前这套框架中不够灵活,因为笔者采用的是微服务框架,考虑到各个服务都有可能使用不同的数据源,而多数据源动态切换是放在公共方法中实现的,如果每有新的数据源就要定义一个,对代码的侵入性太高,在c#中,选择数据源很容易,根据连接名称就可以切换过去,如下所示:

能不能像c#这样根据连接名称就自动选择呢,笔者的连接配置如下所示:

spring:

application:

name: csg-auth

datasource:

kbase:

- driverClassName: com.kbase.jdbc.Driver

jdbcUrl: jdbc:kbase://127.0.0.1

username: DBOWN

password:

jdbc:

- driverClassName: com.mysql.cj.jdbc.Driver

jdbcUrl: jdbc:mysql://localhost:3306/nacos?serverTimezone=GMT%2B8&useUnicode=false&characterEncoding=utf8&useSSL=false

username: root

password: 123456

connName: nacos

- driverClassName: com.mysql.cj.jdbc.Driver

jdbcUrl: jdbc:mysql://localhost:3306/tpi?serverTimezone=GMT%2B8&useUnicode=false&characterEncoding=utf8&useSSL=false

username: root

password: 123456

connName: tpi

其中kbase不用理会,是我们公司自己的数据库,jdbc是维护的连接集合,其中connName就是我们自定义的连接名称,

根据connName就可以自动切换到对应数据源。

笔者实现代码如下:

第一步

首先,编写DynamicDataSource类集成AbstractRoutingDataSource,重写determineCurrentLookupKey方法,该方法主要作用是选择数据源的key

代码如下:

/**

* 动态数据源

* */

public class DynamicDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

return DataSourceHolder.getDataSource();

}

}

第二步

第二部编写DataSourceHolder类,提供设置、获取、情况数据源的方法,如下所示:

public class DataSourceHolder {

/**

* 线程本地环境

*/

private static final ThreadLocal dataSources = new ThreadLocal();

/**

* 设置数据源

*/

public static void setDataSources(String connName) {

dataSources.set(connName);

}

/**

* 获取数据源

*/

public static String getDataSource() {

return dataSources.get();

}

/**

* 清楚数据源

*/

public static void clearDataSource() {

dataSources.remove();

}

}

第三步

第三步,编写DataSourceConfig类,该类主要作用是读取配置文件中的数据源连接集合,以及维护项目数据源的Bean对象,

代码如下:

@Component

@ConfigurationProperties("spring.datasource")

public class DataSourceConfig {

private List jdbc;

public Map getDataSourceMap(){

Mapmap=new HashMap<>();

if (jdbc!=null&&jdbc.size()>0){

for (int i = 0; i < jdbc.size() ; i++) {

DataSourceBuilder dataSourceBuilder=DataSourceBuilder.create();

dataSourceBuilder.driverClassName(jdbc.get(i).getDriverClassName());

dataSourceBuilder.password(jdbc.get(i).getPassword());

dataSourceBuilder.username(jdbc.get(i).getUsername());

dataSourceBuilder.url(jdbc.get(i).getJdbcUrl());

map.put(jdbc.get(i).getConnName(),dataSourceBuilder.build());

}

}

return map;

}

@Bean

public DataSource csgDataSource(){

DynamicDataSource dynamicDataSource=new DynamicDataSource();

MapdataSourceMap=getDataSourceMap();

dynamicDataSource.setTargetDataSources(dataSourceMap);

OWoozXvbject object= dataSourceMap.values().toArray()[0];

dynamicDataSource.setDefaultTargetDataSource(object);

return dynamicDataSource;

}

public void setJdbc(List jdbc) {

this.jdbc = jdbc;

}

public List getJdbc(){

return this.jdbc;

}

}

其中,getDataSourceMap()方法,作用是根据配置的连接集合,生成AbstractRoutingDataSource所需要的resolvedDataSources。

而csgDataSource()方法,添加了@Bean注解,作用是让mybatis的SqlSessionFactory,能够使用咱们维护的数据源。

第四部

编写MyBatisConfig类,该类主要作用是 配置好mybatis的数据源。

@Configuration

public class MyBatisConfig {

@Autowired

private DataSource csgDataSource;

@Bean

public SqlSessionFactory sqlSessionFactory() throws Exception {

SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();

sqlSessionFactoryBean.setDataSource(csgDataSource);

sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()

.getResources("classpath:mapper/**/*.xml"));

return sqlSessionFactoryBean.getObject();

}

@Bean

public PlatformTransactionManager platformTransactionManager(){

return new DataSourceTransactionManager(csgDataSource);

}

}

可以看到,这里选择的是我们定义好的csgDataSource,其作用也是如此。

第五步

编写TargetDataSource注解

/**

* 注解标签

* 作用于 方法、接口、类、枚举、注解

* */

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD,ElementType.TYPE})

public @interface TargetDataSource {

String connName();

}

其中connName,就是我们需要使用的数据源

第六步

编写DataSourceExchange,改类为切面,作用于TargetDataSource注解,故使用TargetDataSource注解的时候,

会根据connName自动选择数据源。

@Aspect

@Component

public class DataSourceExchange {

@Before("@annotation(TargetDataSource)")

public void before(JoinPoint joinPoint){

MethodSignature sign = (MethodSignature) joinPoint.getSignature();

Method method = sign.getMethod();

boolean isMethodAop= method.isAnnotationPresent(TargetDataSource.class);

if (isMethodAop){

TargetDataSource datasource = method.getAnnotation(TargetDataSource.class);

DataSourceHolder.setDataSources(datasource.connName());

}else {

if (joinPoint.getTarget().getClass().isAnnotationPresent(TargetDataSource.class)){

TargetDataSource datasource = joinPoint.getTarget().getClass().getAnnotation(TargetDataSource.class);

DataSourceHolder.setDataSources(datasource.connName());

}

}

}

@After("@annotation(TargetDataSource)")

public void after(){

DataSourceHolder.clearDataSource();

}

}

改切面作用于方法运行前后,负责选择、取消数据源。

第七部

开始验证,使用方法如下:

@Service

public class UserServiceImpl implements UserService {

@Autowired

private UserMapper userMapper;

@Override

@TargetDataSource(connName = "nacos")

public List getList() {

List list= userMapper.selectUserList();

return list;

}

}

在service中,在需要进行数据库操作的方法上,添加TargetDataSource注解,即可自动切换到所需要的数据源。

至此,mybatis就可以动态切换数据源了。

笔者从事java开发工作不多,改方法可能不是太好,也请各位看官勿喷~

总结

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Win10系统下配置java环境变量的全过程
下一篇:JavaWeb入门教程之分页查询功能的简单实现
相关文章

 发表评论

暂时没有评论,来抢沙发吧~