springboot多数据源配置实例

本文介绍了如何在SpringBoot项目中使用Mybatis配置多个数据源,包括数据源名称的自定义、DataSourceType枚举、DataSourceUtil工具类以及如何动态选择数据源。还涉及到了会话工厂和事务管理的配置。
摘要由CSDN通过智能技术生成

context-path: /db

指定Mybatis的Mapper文件

mybatis:

mapper-locations: classpath:mappers/*xml

type-aliases-package: jin.panpan.database.entity

#日志配置

logging:

config: classpath:log4j2.xml

复制代码

配置文件, 除了基本的应用名称、端口、路径、日志等配置以外, 主要看数据库驱动的配置, 这里我们不像平时的单数据源的配置, 我们需要配置多个数据源, 上面示例的配置文件里, 由两个注意点:

  • db1和db2是我们自定义的数据源名称, 这个我们在后面的MybatisConfig文件里会用到

  • 数据源里的配置jdbc-url对应单数据源配置里的url, 这里使用url会注入失败

  • ${db1.username}等是导入的application-jdbc.properties文件里的配置值

application-jdbc.properties

db1.username = user

db1.password =

db1.url = jdbc:mysql://xx.xx.xx.xx:3306/db1

db2.username = user

db2.password =

db2.url = jdbc:mysql://xx.xx.xx.xx:3306/db2

复制代码

key-value结构, key可以在引入此文件的配置里使用${key}引用

这里我们使用两个数据库, 但是数据库的表结构一致(至少多数据源涉及到的表需要结构一致), 以避免不能共用一套代码

DataSourceType

public enum DataSourceType {

//NONE用来返回默认

NONE(“”),

DB1(“db1”),

DB2(“db2”),

;

//省略部分代码

}

复制代码

此文件是数据源的枚举, 这里的枚举我们只配置里一个参数, 可以依据业务扩展. 本枚举的值代表数据源的名称, 是和配置文件里的配置对应上的

另外说明一下, NONE是用来代表默认数据源的, 是为了方便编程添加的, 不是必须的

DataSourceUtil

public class DataSourceUtil {

private static final ThreadLocal localDataSource = new ThreadLocal<>();

private DataSourceUtil(){

}

public static DataSourceType get() {

return localDataSource.get();

}

public static void set(DataSourceType type){

localDataSource.set(type);

}

public static void remove() {

localDataSource.remove();

}

}

复制代码

数据源切换工具类, 这里的核心是ThreadLocal<DataSourceType>变量, ThreadLocal以前有过文档分析, 主要是用来维护线程内部变量的, 其中:

  • get()方法用来获取数据源

  • set()方法用来设置数据源

  • remove()用来清除数据源

MybatisConfig*

@Configuration

@MapperScan(“jin.panpan.database.dao”)

public class MybatisConfig {

//默认数据源

@Primary

@Bean(“db1”)

@ConfigurationProperties(prefix = “spring.datasource.db1”)

public DataSource dataSource1(){

return DataSourceBuilder.create().build();

}

@Bean(“db2”)

@ConfigurationProperties(prefix = “spring.datasource.db2”)

public DataSource dataSource2(){

return DataSourceBuilder.create().build();

}

//动态数据源选择

@Bean

public DynamicDataSource dynamicDataSource(@Qualifier(“db1”) DataSource db1,

@Qualifier(“db2”) DataSource db2){

Map<Object, Object> map = new HashMap<>();

//此处的key 要和DynamicDataSource类的determineCurrentLookupKey方法返回值一致

map.put(DataSourceType.DB1.getDatabase(), db1);

map.put(DataSourceType.DB2.getDatabase(), db2);

DynamicDataSource dynamicDataSource = new DynamicDataSource();

dynamicDataSource.setTargetDataSources(map);

//设置默认数据库, 选择的数据库Bean注入时要带@Primary注释

dynamicDataSource.setDefaultTargetDataSource(db1);

return dynamicDataSource;

}

//会话工厂配置

@Bean

public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {

SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

factoryBean.setDataSource(dynamicDataSource);

Resource[] resources = new PathMatchingResourcePatternResolver()

.getResources(“classpath:mappers/*xml”);

factoryBean.setMapperLocations(resources);

return factoryBean.getObject();

}

//事务管理配置

@Bean

public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource){

return new DataSourceTransactionManager(dynamicDataSource);

}

}

复制代码

本类主要用来配置和注入mybatis相关配置bean, 主要有下面几步:

  • 自动扫描mapper接口配置

@MapperScan(“jin.panpan.database.dao”)

一般常规配置, 如果不配置多数据源, 一般是在入口类上注解

  • 数据源注入

@ConfigurationProperties 导入配置文件里的配置

@Bean 指定数据源名称

@Primary 标记默认数据源

  • 数据源动态选择Bean注入

加载多个数据源, 如果需要的话, 指定默认数据源

需要注意数据源容器Map的key要和DataSourceType里的字段值一致, 否则无法切换

  • 会话工厂Bean配置

单数据源时, 一般无需手动配置; 多数据源时, 需要手动指定会话工厂, 但是配置是模式化的

  • 事务管理Bean配置

事务管理的配置也是模式化的, 一般无特殊处理之处

DynamicDataSource*

public class DynamicDataSource extends AbstractRoutingDataSource {

private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

@Override

protected Object determineCurrentLookupKey() {

String database = DataSourceUtil.get() == null ? null : DataSourceUtil.get().getDatabase();

logger.info(“DynamicDataSource, 动态数据源返回={}”, database);

return database;

}

}

复制代码

此类其实是动态路由数据源的核心类, 扩展AbstractRoutingDataSource类, 重写determineCurrentLookupKey方法, determineCurrentLookupKey方法的返回值就是动态数据源, 返回值需要和配置文件及MybatisConfig里的一一对应.

DataSource和DataSourceAspect*

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Documented

public @interface DataSource {

DataSourceType value() default DataSourceType.NONE;

}

复制代码

DataSource是一个方法注解, 用来作为数据源切换切入点, 此处可以使用类注解或者直接正则织入想监控的点

DataSourceAspect是注解的实现, 是核心实现

@Aspect

@Component

public class DataSourceAspect {

private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

@Pointcut(“@annotation(jin.panpan.database.dynamic.datasource.annotate.DataSource)”)

public void pointCut() {

}

@SneakyThrows

@Around(“pointCut()”)

public Object around(ProceedingJoinPoint pjp) {

//数据源

DataSourceType dataSourceType = null;

//方法对象

MethodSignature signature = (MethodSignature) pjp.getSignature();

Method method = signature.getMethod();

//注释

DataSource dynamicDataSource = method.getAnnotation(DataSource.class);

String path = null;

//注释中指定数据源, 则使用数据库中的数据源

if(dynamicDataSource.value()!=null && dynamicDataSource.value() != DataSourceType.NONE){

dataSourceType = dynamicDataSource.value();

}

//否则从请求路径中获取

if(dataSourceType == null){

HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

path = request.getRequestURL().toString();

//TODO 待优化 需截取域名进行检查

if(path.contains(DataSourceType.DB1.getDatabase())){

dataSourceType = DataSourceType.DB1;

}

if(path.contains(DataSourceType.DB2.getDatabase())){

dataSourceType = DataSourceType.DB2;

}

}

logger.info(“DataSource, 方法={}, 数据源={}, 路径={}, 注释自带数据源={}”,

method.getName(), dataSourceType==null ? null : dataSourceType.getDatabase(),

path, dynamicDataSource.value());

if(dataSourceType == null){

throw new Exception(“非法数据源”);

先自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以扫码领取!

img

总结

对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)

image

Java面试精选题、架构实战文档

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断!
(img-TjOgwKxJ-1711452961493)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以扫码领取!

img

总结

对于面试还是要好好准备的,尤其是有些问题还是很容易挖坑的,例如你为什么离开现在的公司(你当然不应该抱怨现在的公司有哪些不好的地方,更多的应该表明自己想要寻找更好的发展机会,自己的一些现实因素,比如对于我而言是现在应聘的公司离自己的家更近,又或者是自己工作到达了迷茫期,想跳出迷茫期等等)

[外链图片转存中…(img-es6WJn9b-1711452961494)]

Java面试精选题、架构实战文档

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断!
需要更多Java资料的小伙伴可以帮忙点赞+关注,点击传送门,即可免费领取!

  • 11
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值