在使用 Spring Boot 和 MyBatis 動態切換數據源時遇到的問題以及解決方法


相關項目地址:https://github.com/helloworlde/SpringBoot-DynamicDataSource

1. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

在使用了動態數據源后遇到了該問題,從錯誤信息來看是因為沒有找到 *.xml 文件而導致的,但是在配置文件中
確實添加了相關的配置,這種錯誤的原因是因為設置數據源后沒有設置SqlSessionFactoryBeantypeAliasesPackage
mapperLocations屬性或屬性無效導致的;

  • 解決方法:

如果在應用的入口類中添加了 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class),
DataSourceConfigure類的中設置相關屬性:

    @Bean
    @ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
return sqlSessionFactoryBean;
}

或者直接配置(不推薦該方式):

    @Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setTypeAliasesPackage("typeAliasesPackage");
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("mapperLocations"));
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
return sqlSessionFactoryBean;
}

2. Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

該異常在錯誤信息中已經說的很清楚了,是因為有多個 DataSource 的實例,所以無法確定該引用那個實例

  • 解決方法:

為數據源的某個 Bean 添加 @Primary 注解,該 Bean 應當是通過 DataSourceBuilder.create().build()
得到的 Bean,而不是通過 new AbstractRoutingDataSource 的子類實現的 Bean,在本項目中可以是 master()
slave() 得到的 DataSource,不能是 dynamicDataSource() 得到的 DataSource

3. 通過注解方式動態切換數據源無效

  • 請確認注解沒有放到 DAO 層方法上, 因為會在 Service 層開啟事務,所以當注解在 DAO 層時不會生效
  • 請確認以下 Bean 正確配置:
    @Bean("dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put("master", master());
dataSourceMap.put("slave", slave());

// Set master datasource as default
dynamicRoutingDataSource.setDefaultTargetDataSource(master());
// Set master and slave datasource as target datasource
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
// To put datasource keys into DataSourceContextHolder to judge if the datasource is exist
DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
}
return dynamicRoutingDataSource;
}
@Bean
@ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// Here is very important, if don't config this, will can't switch datasource
// put all datasource into SqlSessionFactoryBean, then will autoconfig SqlSessionFactory
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
return sqlSessionFactoryBean;
}

4. @Transactional 注解無效,發生異常不回滾

  • 請確認該 Bean 得到正確配置,並且@TransactionalrollbackFor 配置正確
    @Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}

5. 通過 AOP 判斷 DAO 層方法名時切換數據源無效

當切面指向了 DAO 層后無論如何設置切面的順序,都無法在執行查詢之前切換數據源,但是切面改為 Service 層后可以正常工作

  • 解決方法: 請確認 @Transactional 注解是加在方法上而不是 Service 類上,添加了 @Transactional 的方法因為在 Service 層開啟了事務,
    會在事務結束之后才會切換數據源

  • 檢出 DataSourceTransactionManager Bean 注入正確

    @Bean 
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}

6. The dependencies of some of the beans in the application context form a cycle

  • 錯誤信息:
The dependencies of some of the beans in the application context form a cycle:
produceController (field private cn.com.hellowood.dynamicdatasource.service.ProductService cn.com.hellowood.dynamicdatasource.controller.ProduceController.productService)

productService (field private cn.com.hellowood.dynamicdatasource.mapper.ProductDao cn.com.hellowood.dynamicdatasource.service.ProductService.productDao)

productDao defined in file [/Users/hehuimin/Downloads/Dev/SpringBoot/DynamicDataSource/out/production/classes/cn/com/hellowood/dynamicdatasource/mapper/ProductDao.class]

sqlSessionFactoryBean defined in class path resource [cn/com/hellowood/dynamicdatasource/configuration/DataSourceConfigurer.class]
┌─────┐
| dynamicDataSource defined in class path resource [cn/com/hellowood/dynamicdatasource/configuration/DataSourceConfigurer.class]


| master defined in class path resource [cn/com/hellowood/dynamicdatasource/configuration/DataSourceConfigurer.class]


| dataSourceInitializer
└─────┘

這是因為在注入 DataSource 的實例的時候產生了循環調用,第一個注入的 Bean 依賴於其他的 Bean, 而被依賴的 Bean 產生依賴傳遞,依賴第一個
注入的 Bean, 陷入了循環,無法啟動項目

  • 解決方法:將 @Primary 注解指向沒有依賴的 Bean,如:

    /**
* master DataSource
* @Primary 注解用於標識默認使用的 DataSource Bean,因為有三個 DataSource Bean,該注解可用於 master
* 或 slave DataSource Bean, 但不能用於 dynamicDataSource Bean, 否則會產生循環調用
* * @ConfigurationProperties 注解用於從 application.properties 文件中讀取配置,為 Bean 設置屬性
* @return data source
*/

@Bean("master")
@Primary
@ConfigurationProperties(prefix = "application.server.db.master")
public DataSource master() {
return DataSourceBuilder.create().build();

} @Bean("slave")
@ConfigurationProperties(prefix = "application.server.db.slave")
public DataSource slave() {
return DataSourceBuilder.create().build();

} @Bean("dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put("master", master());
dataSourceMap.put("slave", slave());

// Set master datasource as default
dynamicRoutingDataSource.setDefaultTargetDataSource(master());
// Set master and slave datasource as target datasource
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);

// To put datasource keys into DataSourceContextHolder to judge if the datasource is exist
DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
return dynamicRoutingDataSource;

}
                                <link rel="stylesheet" href="http://csdnimg.cn/release/phoenix/production/markdown_views-10f5517761.css">
                                </div>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM