spring boot 配置多個數據源
程式碼實現
1. 修改 application.yml
配置檔案,新增db_2
資料庫連線
注意:配置連線兩個資料庫,url
改為:jdbc-url
server: port: 8083 servlet: context-path: /mes spring: db1: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/lmes username: root password: root type: com.alibaba.druid.pool.DruidDataSource db2: datasource: driver-class-name: oracle.jdbc.OracleDriver url: jdbc:oracle:thin:@//172.20.10.101:1521/PROD username: apps password: apps type: com.alibaba.druid.pool.DruidDataSource
2. 新建config
包,新增db1
和db2
的配置檔案
- 主資料庫與從資料庫配置區別:主資料庫有
@Primary
註解,從資料庫都沒有
2.1 主資料庫db1,專案啟動預設連線此資料庫:PrimaryDataSourceConfig
package com.bbzd.mes.common.datasources; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.JdbcType; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.boot.autoconfigure.MybatisProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; import java.io.IOException; import java.util.Optional; import java.util.stream.Stream; @Configuration @EnableConfigurationProperties(MybatisProperties.class) public class PrimaryDataSourceConfig { @Autowired private MybatisProperties properties; @Bean(name = "db1DataSource") @ConfigurationProperties(prefix = "spring.db1.datasource") @Primary public DataSource db1DataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "sqlSessionFactory") @ConfigurationProperties(prefix = "mybatis") @Primary public SqlSessionFactory sqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setTypeAliasesPackage(properties.getTypeAliasesPackage()); bean.setMapperLocations(resolveMapperLocations(properties.getMapperLocations())); properties.getConfiguration().setJdbcTypeForNull(JdbcType.NULL); bean.setConfiguration(properties.getConfiguration()); return bean.getObject(); } private Resource[] resolveMapperLocations(String[] mapperLocations) { PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver(); return Stream.of(Optional.ofNullable(mapperLocations).orElse(new String[0])) .flatMap(location -> Stream.of(getResources(pathMatchingResourcePatternResolver, location))).toArray(Resource[]::new); } private Resource[] getResources(PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver, String location) { try { return pathMatchingResourcePatternResolver.getResources(location); } catch (IOException e) { return new Resource[0]; } } @Bean(name = "db1TransactionManager") @Primary public DataSourceTransactionManager testTransactionManager(@Qualifier("db1DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
2.2 從資料庫db2:DataSource2Config
package com.bbzd.mes.common.datasources; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = "com.bbzd.mes.oracledao",sqlSessionFactoryRef = "db2SqlSessionFactory") public class DataSource2Config { @Bean(name = "db2DataSource") @ConfigurationProperties(prefix = "spring.db3.datasource") public DataSource db3DataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "db2SqlSessionFactory") public SqlSessionFactory db2SqlSessionFactory(@Qualifier("db2DataSource") DataSource datasource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(datasource); bean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:oracle_mappers/*.xml")); return bean.getObject(); } @Bean(name="db2transactionManager") public DataSourceTransactionManager transactionManagerOne(){ return new DataSourceTransactionManager(db3DataSource()); } @Bean(name = "db2JdbcTemplate") public JdbcTemplate jdbcTemplate( @Qualifier("db2DataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } }
3. dao層
注意:主資料庫與從資料庫區別:從資料庫需要使用@Transactional
註解指向db2
資料來源
3.1 主資料庫db1,dao層寫法
public interface UserMapper {
boolean save(UserVo userVo);
}
3.2 從資料庫db2,dao層寫法
@Transactional(value = "db2transactionManager")
public interface UserMapper {
UserDto findById(Inter id);
}
4. resources目錄下xml注意事項
db1 和 db2 xml
檔案 namespace
不同
-
資料庫db1,User.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.bbzd.mes.dao.UserMapper"> </mapper>
-
資料庫db2,User.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.bbzd.mes.oracledao.UserMapper"> </mapper>
開發中遇到的問題
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: more than one 'primary' bean found among candidates: [db1DataSource, primaryDataSource]
多資料來源報錯:No qualifying bean of type 'javax.sql.DataSource' available: more than one 'primary' bean found among candidates: [test2DataSource, test1DataSource]由於之前引入mybatis的時候引入了pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
spring會依賴spring-boot-autoconfigure這個jar包
這個jar包中 有個DataSourceAutoConfiguration 會初始化DataSourceInitializer 這個類 ,這個類有一個init方法 會去獲取DataSource(資料來源)
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration
@ConditionalOnMissingBean(DataSourceInitializer.class)
protected static class DataSourceInitializerConfiguration {
@Bean
public DataSourceInitializer dataSourceInitializer() {
return new DataSourceInitializer();
}
初始化方法中 會獲取資料來源 需要初始化一些ddl操作 也是就runSchemaScripts()方法 檢查初始化時是否需要執行sql script ,當你有兩個資料來源的時候,程式不知道取哪一個 ,所以報錯
@PostConstruct
public void init() {
if (!this.properties.isInitialize()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return;
}
if (this.applicationContext.getBeanNamesForType(DataSource.class, false, false).length > 0) {
this.dataSource = this.applicationContext.getBean(DataSource.class);
}
if (this.dataSource == null) {
logger.debug("No DataSource found so not initializing");
return;
}
runSchemaScripts();
}
private void runSchemaScripts() {
List<Resource> scripts = getScripts(this.properties.getSchema(), "schema");
if (!scripts.isEmpty()) {
runScripts(scripts);
try {
this.applicationContext.publishEvent(new DataSourceInitializedEvent(
this.dataSource));
// The listener might not be registered yet, so don't rely on it.
if (!this.initialized) {
runDataScripts();
this.initialized = true;
}
}
catch (IllegalStateException ex) {
logger.warn("Could not send event to complete DataSource initialization ("
+ ex.getMessage() + ")");
}
}
}
解決辦法:
spring boot 啟動類加上 exclude = DataSourceAutoConfiguration.class 代表啟動專案的時候 不載入這個類
@ComponentScan(basePackages = "com.pinyu.system")
@MapperScan("com.pinyu.system.mapper")
@EnableTransactionManagement
@SpringBootApplication(exclude={
DataSourceAutoConfiguration.class,
// HibernateJpaAutoConfiguration.class, //(如果使用Hibernate時,需要加)
DataSourceTransactionManagerAutoConfiguration.class,
})
public class Application extends SpringBootServletInitializer {
}
原文連結:https://blog.csdn.net/github_38336924/article/details/112789455
原文連結:https://blog.csdn.net/xiaoanzi123/article/details/105094059/