1. 程式人生 > 其它 >spring boot 配置多個數據源

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包,新增db1db2的配置檔案

  • 主資料庫與從資料庫配置區別:主資料庫有 @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/