Java Springboot 服务连接两个数据库

前言

遇到的场景:从 B 服务中大批量存入数据库 db2,从A 服务中读取 db2 数据库中的部分数据做处理再存入 A 服务所对的主数据库 db1,所以 A 服务需要连接两个数据库 db1,db2

配置文件

在 application.properties 文件中做数据库连接的配置,
默认的 spring boot框架中连接数据库有一套已有的 配置,如下:

spring.datasource.username=...
spring.datasource.password=...
spring.datasource.url=...
spring.datasource.driver-class-name-...

这是 Springboot 框架中默认设置了数据库的集成配置,SpringBoot 的启动类中,一般都会有 @SpringBootApplication 注解,这个注解中,内嵌了 @EnableAutoConfiguration 注解类,这个类中会找到 MATE-INF/spring-autoconfigure-metadata.properties 配置文件,看此配置文件中,包含了默认配置各种操作,包括 datasource……
所以,如果我们要 使用一个服务连接两个数据库,那就无法使用 框架中默认配置的一套,首先在启动类中先做不启动默认数据库的配置

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class TestApplication{
	...
}

在配置文件中配置如下:

<!-- 连接数据库 db1 -->
spring.datasource.db1.username=root
spring.datasource.db1.password=admin
spring.datasource.db1.jdbc-url=jdbc:mysql://localhost:3306/db1?serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&allowMultiqueries=true&allowPublicKeyRetrival=true&allowLoadInfile=true
spring.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
<!-- 连接数据库 db2 -->
spring.datasource.db2.username=root
spring.datasource.db2.password=admin
spring.datasource.db2.jdbc-url=jdbc:mysql://localhost:3306/db2?serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&allowMultiqueries=true&allowPublicKeyRetrival=true&allowLoadInfile=true
spring.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver

既然 禁用了框架中原始的配置,我们需要自定义我们的配置,所以有以下配置类DB1Config ,设置了连接 db1 库的配置

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "org.example.db1.**.mapper",sqlSessionFactoryRef = "db1SqlSessionFactory")
public class DB1Config {
    
    @Primary
    @Bean(name = "db1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource initDataSource(){
        return DataSourceBuilder.create().build();
    }
    
    @Primary
    @Bean(name = "db1SqlSessionFactory")
    public SqlSessionFactory initSqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setDbConfig(new GlobalConfig.DbConfig().setUpdateStrategy(FieldStrategy.IGNORED));
        bean.setGlobalConfig(globalConfig);
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisConfiguration.setMapUnderscoreToCamelCase(false);
        bean.setConfiguration(mybatisConfiguration);
        bean.setPlugins(new Interceptor[]{mybatisPlusInterceptor()});
        SqlSessionFactory sessionFactory = bean.getObject();
        org.apache.ibatis.session.Configuration configuration = sessionFactory.getConfiguration();
        configuration.setMapUnderscoreToCamelCase(false);
        return sessionFactory;
    }

    @Primary
    @Bean(name = "db1mybatisPlusInterceptor")
    public Interceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
    
    
    @Primary
    @Bean(name = "db1DataSourceTransactionManager")
    public DataSourceTransactionManager initDataSourceTransactionManager(@Qualifier("db1DataSource") DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    @Primary
    @Bean(name = "db1SqlSessionTemplate")
    public SqlSessionTemplate initSqlSessionTemplate(@Qualifier("db1SqlSessionFactory")SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }
    
}

同样,数据库 db2 的配置连接也类似,只不过 要将 @Primary 注解去掉

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
@Configuration
@MapperScan(basePackages = "org.example.db2.**.mapper",sqlSessionFactoryRef = "db2SqlSessionFactory")
public class DB2Config {

    @Bean(name = "db2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource initDataSource(){
        return DataSourceBuilder.create().build();
    }
    
    @Bean(name = "db2SqlSessionFactory")
    public SqlSessionFactory initSqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setDbConfig(new GlobalConfig.DbConfig().setUpdateStrategy(FieldStrategy.IGNORED));
        bean.setGlobalConfig(globalConfig);
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisConfiguration.setMapUnderscoreToCamelCase(false);
        bean.setConfiguration(mybatisConfiguration);
        bean.setPlugins(new Interceptor[]{mybatisPlusInterceptor()});
        SqlSessionFactory sessionFactory = bean.getObject();
        org.apache.ibatis.session.Configuration configuration = sessionFactory.getConfiguration();
        configuration.setMapUnderscoreToCamelCase(false);
        return sessionFactory;
    }

  
    @Bean(name = "db2mybatisPlusInterceptor")
    public Interceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }



    @Bean(name = "db2DataSourceTransactionManager")
    public DataSourceTransactionManager initDataSourceTransactionManager(@Qualifier("db2DataSource") DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
    
    @Bean(name = "db2SqlSessionTemplate")
    public SqlSessionTemplate initSqlSessionTemplate(@Qualifier("db2SqlSessionFactory")SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

还要注意 @MapperScan 注解 扫描的mapper 文件的路径,最好将两个数据库操作的 mapper 文件分到不同的文件夹中。
其中,initSqlSessionFactory 中的一些配置,可以根据自定义的需求设置,比较灵活的。

项目启动后,可以在 控制台中看到

HikariPool-1 - Starting...
HikariPool-1 - Start completed.
HikariPool-2 - Starting...
HikariPool-2 - Start completed.

表示两个数据库都连接上了
其他 mapper 类正常使用即可。

ok,结束。

更多推荐