前言:在项目中需要用到动态切换多数据源,查阅Mybatis-plus文档得知可以通过@DS注解,但该方法主要针对不同内容的数据源,而目前场景是相同内容的数据库需要在运行时根据请求头动态切换,因此文档方法不适用。
注意,不要使用dynamic-datasource-spring-boot-starter依赖包。
一、动态多数据源的场景
- 应用不拆,数据库拆
- 读写分离
二、动态多数据源的实现
网上文章非常多,大体思路都差不多,笔者在这里不重复放置代码了,例如:《springboot 中动态切换数据源》
不过目前找到的文章方法在项目整合了Mybatis-plus的情况下基本都有问题,以下是这几天遇到的问题和解决方案。
三、问题与解决方案
1. 找不到事务管理器
No qualifying bean of type 'org.springframework.transaction.TransactionManager
可以参照《mybatis-plus多数据源事务报错 》,在Springboot中,每一个事务管理器都需要对应一个datasource,而多数据源操作时,需要在@Transactional注解中指定事务管理器,或者配置默认事务管理器。
不过笔者在《springboot动态切换多个数据源》中发现,可以直接将自定义的dynamicDataSource作为dataSource传给事务管理器,并设置为默认事务管理器,这样就不用配置多个事务管理器了,代码如下:
@Bean(name ="platformTransactionManager")@PrimarypublicPlatformTransactionManagerplatformTransactionManager(){PlatformTransactionManager transactionManager =newDataSourceTransactionManager(dynamicDataSource());return transactionManager;}
2. DataSource没有初始化
DataSource router not initialized
这个问题比较奇特,具体解决方法是在stack overflow中看到的。在自定义的DynamicDataSource类的实现方法中添加如下一行代码:
dataSource.afterPropertiesSet();
该方法目的是让Bean设置好所有属性后再执行初始化操作。
3. 找不到数据源
Invalid bound statement (not found)
这个问题是最坑的,具体原因参照《mybatis升级为mybatis-plus踩到的坑》,而网上很多多数据源文章都没有提到这一点。解决方法也很简单,就是将配置的SqlSessionFactory改为MybatisSqlSessionFactoryBean。
@BeanpublicMybatisSqlSessionFactoryBeanSqlSessionFactory()throwsException{MybatisSqlSessionFactoryBean sqlSessionFactoryBean =newMybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());// dynamicDataSource()为自己定义的实现方法
sqlSessionFactoryBean.setMapperLocations(newPathMatchingResourcePatternResolver().getResources("classpath*:Mapper/*.xml")// 填写自己的XML路径);return sqlSessionFactoryBean;}
该问题的起源,也是因为导入了Mybatis plus才出现的,导入mybatis-plus-boot-starter依赖包,可以看到有一个MybatisPlusAutoConfiguration类。
点进去,发现有一个这样的Bean,以及注解
TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
,以下都是源码。所以可以知道,在Mybatis plus框架中,使用的都是封装后的MybatisSqlSessionFactoryBean。
@Bean@ConditionalOnMissingBeanpublicSqlSessionFactorysqlSessionFactory(DataSource dataSource)throwsException{// TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBeanMybatisSqlSessionFactoryBean factory =newMybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);if(StringUtils.hasText(this.properties.getConfigLocation())){
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));}applyConfiguration(factory);if(this.properties.getConfigurationProperties()!=null){
factory.setConfigurationProperties(this.properties.getConfigurationProperties());}if(!ObjectUtils.isEmpty(this.interceptors)){
factory.setPlugins(this.interceptors);}if(this.databaseIdProvider !=null){
factory.setDatabaseIdProvider(this.databaseIdProvider);}if(StringUtils.hasLength(this.properties.getTypeAliasesPackage())){
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());}if(this.properties.getTypeAliasesSuperType()!=null){
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());}if(StringUtils.hasLength(this.properties.getTypeHandlersPackage())){
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());}if(!ObjectUtils.isEmpty(this.typeHandlers)){
factory.setTypeHandlers(this.typeHandlers);}if(!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())){
factory.setMapperLocations(this.properties.resolveMapperLocations());}// TODO 对源码做了一定的修改(因为源码适配了老旧的mybatis版本,但我们不需要适配)Class<?extendsLanguageDriver> defaultLanguageDriver =this.properties.getDefaultScriptingLanguageDriver();if(!ObjectUtils.isEmpty(this.languageDrivers)){
factory.setScriptingLanguageDrivers(this.languageDrivers);}Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);// TODO 自定义枚举包if(StringUtils.hasLength(this.properties.getTypeEnumsPackage())){
factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());}// TODO 此处必为非 NULLGlobalConfig globalConfig =this.properties.getGlobalConfig();// TODO 注入填充器if(this.applicationContext.getBeanNamesForType(MetaObjectHandler.class,false,false).length >0){MetaObjectHandler metaObjectHandler =this.applicationContext.getBean(MetaObjectHandler.class);
globalConfig.setMetaObjectHandler(metaObjectHandler);}// TODO 注入主键生成器if(this.applicationContext.getBeanNamesForType(IKeyGenerator.class,false,false).length >0){IKeyGenerator keyGenerator =this.applicationContext.getBean(IKeyGenerator.class);
globalConfig.getDbConfig().setKeyGenerator(keyGenerator);}// TODO 注入sql注入器if(this.applicationContext.getBeanNamesForType(ISqlInjector.class,false,false).length >0){ISqlInjector iSqlInjector =this.applicationContext.getBean(ISqlInjector.class);
globalConfig.setSqlInjector(iSqlInjector);}// TODO 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean
factory.setGlobalConfig(globalConfig);return factory.getObject();}
4. jdbc not connection问题
该问题主要需要检查配置文件,尤其是url要改成jdbc-url,格式如下
spring:datasource:database01:jdbc-url:username:password:driver-class-name:database02:jdbc-url:username:password:driver-class-name:
版权归原作者 西宇•利昂 所有, 如有侵权,请联系我们删除。