在我们日常开发中大多数情况基本都是使用一个数据库就完事了,但是也难免会遇到多数据库的情况,这个时候多数据源就派上用场了。当然了,多数据源有很多的ORM框架都提供了方案,比如MybatisPlus,MybatisFlex等,但是作为开发者的我们不仅要学会用轮子,也要知道一二吧,不能老拿着别人的轮子跑,不去了解和学习其原理吧。那么接下来,我就基于springboot分享一下如何定义自己的多数据源。
第一步: 搭建springboot项目,配置yml, 比如我这里就定义两个数据源即:db1与db2,注意这里和springboot默认数据源配置有区别哦,是jdbc-url不是url。
spring:
datasource:
db1:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8
username: 你的用户名
password: 你的密码
db2:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8
username: 你的用户名
password: 你的密码
第二步: 开始撸码
1.定义数据源枚举,如还有可以继续定义。
public enum DataSourceType {
//数据源1
DB1,
//数据源2
DB2;
}
- 定义一个数据源切换上下文辅助类
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> LOCAL = new ThreadLocal<>();
// 设置数据源
public static void setDataSourceType(String type) {
LOCAL.set(type);
}
//获取数据源
public static String getDataSourceType() {
return LOCAL.get();
}
//清除数据源
public static void clearDataSourceType() {
LOCAL.remove();
}
}
3.定义一个名为DynamicDatasource的类继承spring提供的AbstractRoutingDataSource类
public class DynamicDataSource extends AbstractRoutingDataSource {
//通过数据源上下文辅助类拿到数据源类型作为理由key
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
- 注入数据源,排除springboot自动配置数据源,不然启动会报找不到数据源的错误
@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class DynamicConfig {
// 数据1
@Bean
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource db1DataSource(){
return DataSourceBuilder.create().build();
}
// 数据2
@Bean
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource db2DataSource(){
return DataSourceBuilder.create().build();
}
//主数据源及数据源列表配置
@Bean
@Primary
public DataSource dynamicDataSource(){
final DynamicDataSource dynamicDataSource = new DynamicDataSource();
final HashMap<Object, Object> targetDataSources = new HashMap<>(2);
final DataSource db1DataSource = db1DataSource();
final DataSource dbed2DataSource = db2DataSource();
targetDataSources.put(DataSourceType.DB1.name(),db1DataSource);
targetDataSources.put(DataSourceType.DB2.name(),dbed2DataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
//设置默认数据源
dynamicDataSource.setDefaultTargetDataSource(db1DataSource);
return dynamicDataSource;
}
}
- 至此就完成了代码的编写,接下来测试多数据源
final List<User> list = userMapper.list();
log.info("=====db1===={}",list);
//通过上下文辅助类切换数据源
DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.DB2.name());
final List<User> list2 = userMapper.list();
log.info("======db2==={}",list2);
运行起能够查询两个数据库的数据就说明ok了,功能上虽然没有问题了,但是还是不够优雅,我们接下来要实现更优雅的数据源切换。
第三步:快码加鞭,使用自定义注解加Aop实现更优雅的数据源切换。
1.定义注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
//默认数据源为db1
DataSourceType value() default DataSourceType.DB1;
}
2.定义aop
@Aspect
@Component
public class DataSourceAop {
/**
* 定义切点为自定义注解
*/
@Pointcut("@annotation(com.example.datasourcedemo.dynamic.annotation.DataSource)")
public void pointCut(){
}
/**
* 切换数据源
* @param point
*/
@Before("pointCut()")
public void changeDataSource(JoinPoint point){
final MethodSignature signature = (MethodSignature) point.getSignature();
final Method method = signature.getMethod();
final DataSource annotation = method.getAnnotation(DataSource.class);
final DataSourceType type = annotation.value();
if(!type.name().equals(DynamicDataSourceContextHolder.getDataSourceType())){
DynamicDataSourceContextHolder.setDataSourceType(type.name());
}
}
/**
* 执行后恢复为默认数据源
* @param point
*/
@After("pointCut()")
public void restDataSource(JoinPoint point){
if(!DataSourceType.DB1.name().equals(DynamicDataSourceContextHolder.getDataSourceType())){
DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.DB1.name());
}
}
- 在测试的mapper上加上注解, 再进行测试
@Mapper
public interface UserMapper {
@Select("select * from user")
List<User> list();
//用上自定义注解
@DataSource(DataSourceType.DB2)
@Select("select * from user")
List<User> list2();
}
去除之前测试代码中的上下文辅助切换方式,再次运行,结果一致就说明ok了。
final List<User> list = userMapper.list();
log.info("=====db1===={}",list);
final List<User> list2 = userMapper.list2();
log.info("=====db2===={}",list2);
总结,通过以上三步就完成了springboot自定义多数据源,是不是也没有想象中那么难。轮子虽好,但是我们作为开发者也要学习实现,不能老是拿来主义,知其然不知其所以然,这样永远也突破不了自己。今天就分享到这里了,有什么问题欢迎留言探讨或批评指正,如果喜欢我的文章记得关注我哦😊!
网友评论