实战 SpringBoot + Mybatis 如何对接多数据源

在我们开发一些具有综合功能的项目时,往往会碰到一种情况,需要同时连接多个数据库,这个时候就需要用到多数据源的设计。而 Spring 与 Myabtis 其实做了多数据源的适配,只需少许改动即可对接多数据源。本期我们就贴近实战,以一个单数据源的 Demo 为例,讲述将其改为多数据源项目的过程,希望大家能有所体会。

一、数据源的定义

数据源(Data Source)是指数据存储的地方,大多数情况是指数据库,不过文件服务器、传感器、API 等也能算数据源,主要是提供了对数据的访问和操作。数据源中存储了所有建立数据库连接的信息。就像通过指定文件名称可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库连接。

二、单数据源配置

因为 SpringBoot 对数据源有着高度的默认配置,只配置一个数据源时,该数据源会被作为默认,所以对接单数据源其实是非常简单的。如果你的工程采用的 yaml 格式配置文件,我们仅需做如下配置:

spring:
  #数据库连接配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/springtest
    username: root
    password: root

如果是采用properties配置文件的也是一样的:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springtest
spring.datasource.username=root
spring.datasource.password=root

三、如何配置多数据源

1. 工程层级调整

我们以曾经搭建的工程为原始模板,进行对接多数据源的操作。没看过的可以点此查看: 从零开始,手把手教你搭建Spring Boot后台工程并说明

因为仅变动数据源,所以我们不改动其他层级,仅仅将 mapper 拆为 mapper1mapper2 两部分

2. Spring 项目配置

然后我们需要在 application.properties 或者 application.yml 中定义多个数据源:

spring:
  #数据库连接配置
  datasource1:
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://127.0.0.1:3306/springtest2
    username: root
    password: root
  datasource2:
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://127.0.0.1:3306/springtest
    username: root
    password: root

这里有两个细节需要注意:

  • 因为我们决定使用双数据源,所以把数据源的连接配置改成了datasource1datasource2。而不再保留datasource,这样 SpringBoot 就不再会为我们设定默认数据库
  • 因为我们目前采用的 springBoot2.5.2,默认的连接池为Hikari,该连接池数据源的地址字段为jdbc-url 而非 url。在只有单个数据源时,SpringBoot 走默认数据源逻辑为我们把 urljdbc-url 进行映射,保证我们获得数据源。此时我们自己设置的数据源没有进行映射处理,就需要保证字段符合Hikari的要求。否则会出现 java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName 异常

3. 会话配置

仅有配置文件可不行,接下来,我们需要在代码中读取到配置,并建立两个数据源。如下,每个数据源都有隔离的 mapper 接口、xml 文件、会话工厂及会话模板

第一个数据源 如下(示例):

@Configuration
@MapperScan(basePackages = "com.nowjava.springboot.demo.mapper1", sqlSessionFactoryRef = "sqlSessionFactory1")
public class DataSource1Config {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource1")
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory1() throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource1());
        String locationPattern = "classpath*:/mapper1/*.xml";
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
        return sessionFactoryBean.getObject();
    }

    @Bean(name = "sqlSessionTemplate1")
    public SqlSessionTemplate sqlSessionTemplate1(@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

配置第二个数据源 DataSource2Config

@Configuration
@MapperScan(basePackages = "com.nowjava.springboot.demo.mapper2", sqlSessionFactoryRef = "sqlSessionFactory2")
public class DataSource2Config {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource2")
    public DataSource dataSource2() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory2() throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource2());
        String locationPattern = "classpath*:/mapper2/*.xml";
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
        return sessionFactoryBean.getObject();
    }

    @Bean(name = "sqlSessionTemplate2")
    public SqlSessionTemplate sqlSessionTemplate2(@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean(name = "productMapper")
    public ProductMapper mapper2(@Qualifier("sqlSessionTemplate2") SqlSessionTemplate sqlSessionTemplate) throws Exception {
        return sqlSessionTemplate.getMapper(ProductMapper.class);
    }
}

4. 事务管理器

为两个数据源分别配置自己的事务管理器,如果你的项目里通篇没有方法级别的事务(一个 SQL 就是一个事务),那不设置这个也不影响,否则还是建议加上。

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java