Spring Boot SQL 多数据源配置

本文介绍 Spring Boot 2 配置多个 SQL 数据源的方法。


目录

  • 开发环境
  • 基础示例
  • 总结

开发环境

  • Oracle JDK 1.8.0_201
  • Apache Maven 3.6.0
  • IntelliJ IDEA (Version 2018.3.3)
  • MySQL 5.6.38

基础示例

  1. 创建 3 个字符集为 utf8 -- UTF-8 Unicode 的 MySQL 数据库 firstsecondthird

  2. 为这 3 个数据库分别创建各自的管理员用户。

2.1 first 数据库管理员:

CREATE USER 'first_admin'@'127.0.0.1' IDENTIFIED BY '111111';

GRANT SELECT, INSERT, UPDATE, REFERENCES, DELETE, CREATE, DROP, ALTER, INDEX, TRIGGER, CREATE VIEW, SHOW VIEW, EXECUTE, ALTER ROUTINE, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, EVENT ON `first`.* TO 'first_admin'@'127.0.0.1';

GRANT GRANT OPTION ON `first`.* TO 'first_admin'@'127.0.0.1';

2.2 second 数据库管理员:

CREATE USER 'second_admin'@'127.0.0.1' IDENTIFIED BY '222222';

GRANT SELECT, INSERT, UPDATE, REFERENCES, DELETE, CREATE, DROP, ALTER, INDEX, TRIGGER, CREATE VIEW, SHOW VIEW, EXECUTE, ALTER ROUTINE, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, EVENT ON `second`.* TO 'second_admin'@'127.0.0.1';

GRANT GRANT OPTION ON `second`.* TO 'second_admin'@'127.0.0.1';

2.3 third 数据库管理员:

CREATE USER 'third_admin'@'127.0.0.1' IDENTIFIED BY '333333';

GRANT SELECT, INSERT, UPDATE, REFERENCES, DELETE, CREATE, DROP, ALTER, INDEX, TRIGGER, CREATE VIEW, SHOW VIEW, EXECUTE, ALTER ROUTINE, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, EVENT ON `third`.* TO 'third_admin'@'127.0.0.1';

GRANT GRANT OPTION ON `third`.* TO 'third_admin'@'127.0.0.1';
  1. 在这 3 个数据库中分表创建 3 张不同含义的表。

3.1 first 数据库中创建客户信息表:

CREATE TABLE `customer` (
`id`  bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`name`  varchar(16) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '姓名' ,
`phone_number`  varchar(16) CHARACTER SET utf8 NULL DEFAULT '' COMMENT '电话号码' ,
PRIMARY KEY (`id`)
)
COMMENT='顾客';

3.2 second 数据库中创建零售商信息表:

CREATE TABLE `retailer` (
`id`  bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`name`  varchar(32) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '名称' ,
`contact`  varchar(64) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '联系方式' ,
PRIMARY KEY (`id`)
)
COMMENT='零售商';

3.3 third 数据库中创建工厂信息表:

CREATE TABLE `factory` (
`id`  bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`name`  varchar(32) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '名称' ,
`address`  varchar(128) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '地址' ,
PRIMARY KEY (`id`)
)
COMMENT='工厂';
  1. 创建 Spring Boot 工程,参考:IntelliJ IDEA 创建 Spring Boot 工程。

  2. 在生成的 pom 文件中添加以下依赖:

  • spring-boot-starter-jdbc:Spring JDBC 支持
  • mysql-connector-java:MySQL 数据库驱动
  • junit:单元测试


    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.4.RELEASE
        
    
    tutorial.spring.boot
    spring-boot-data-source
    0.0.1-SNAPSHOT
    spring-boot-data-source
    Spring Boot Multiple Data Source

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter
        
        
            org.springframework.boot
            spring-boot-starter-jdbc
        
        
            mysql
            mysql-connector-java
            5.1.47
        
        
            junit
            junit
            4.12
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


  1. 在 application.yml 中添加数据源配置。
custom:
  datasource:
    first:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/first?characterEncoding=utf-8&useSSL=true
      username: first_admin
      password: 111111
    second:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/second?characterEncoding=utf-8&useSSL=true
      username: second_admin
      password: 222222
    third:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/third?characterEncoding=utf-8&useSSL=true
      username: third_admin
      password: 333333
  1. 编写数据源配置类。

7.1 first 数据源配置类:

package tutorial.spring.boot.data.source.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

@Configuration
public class FirstDataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties("custom.datasource.first")
    public DataSourceProperties firstDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    public DataSource firstDataSource() {
        return firstDataSourceProperties()
                .initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
    }

    @Bean
    public JdbcOperations firstJdbcOperations() {
        return new JdbcTemplate(firstDataSource());
    }

    @Bean
    public DataSourceTransactionManager firstDataSourceTransactionManager() {
        return new DataSourceTransactionManager(firstDataSource());
    }
}

7.2 second 数据源配置类:

package tutorial.spring.boot.data.source.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

@Configuration
public class SecondDataSourceConfig {

    @Bean
    @ConfigurationProperties("custom.datasource.second")
    public DataSourceProperties secondDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public DataSource secondDataSource() {
        return secondDataSourceProperties()
                .initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
    }

    @Bean
    public JdbcOperations secondJdbcOperations() {
        return new JdbcTemplate(secondDataSource());
    }

    @Bean
    public DataSourceTransactionManager secondDataSourceTransactionManager() {
        return new DataSourceTransactionManager(secondDataSource());
    }
}

7.3 third 数据源配置类:

package tutorial.spring.boot.data.source.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

@Configuration
public class ThirdDataSourceConfig {

    @Bean
    @ConfigurationProperties("custom.datasource.third")
    public DataSourceProperties thirdDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public DataSource thirdDataSource() {
        return thirdDataSourceProperties()
                .initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
    }

    @Bean
    public JdbcOperations thirdJdbcOperations() {
        return new JdbcTemplate(thirdDataSource());
    }

    @Bean
    public DataSourceTransactionManager thirdDataSourceTransactionManager() {
        return new DataSourceTransactionManager(thirdDataSource());
    }
}
  1. 编写映射数据表的领域模型类。

8.1 映射 first 数据库 customer 表的领域模型类:

package tutorial.spring.boot.data.source.domain;

public class Customer {

    /**
     * 数据库表 customer 列名
     */
    public static class ColumnConstant {

        public static final String ID = "id";

        public static final String NAME = "name";

        public static final String PHONE_NUMBER = "phone_number";
    }

    private Long id;

    private String name;

    private String phoneNumber;

    public Customer(String name, String phoneNumber) {
        this.name = name;
        this.phoneNumber = phoneNumber;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Customer customer = (Customer) o;
        if (name != null ? !name.equals(customer.name) : customer.name != null) {
            return false;
        }
        return phoneNumber != null ? phoneNumber.equals(customer.phoneNumber) : customer.phoneNumber == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (phoneNumber != null ? phoneNumber.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", phoneNumber='" + phoneNumber + '\'' +
                '}';
    }
}

8.2 映射 second 数据库 retailer 表的领域模型类:

package tutorial.spring.boot.data.source.domain;

public class Retailer {

    /**
     * 数据库表 retailer 列名
     */
    public static class ColumnConstant {

        public static final String ID = "id";

        public static final String NAME = "name";

        public static final String CONTACT = "contact";
    }

    private Long id;

    private String name;

    private String contact;

    public Retailer(String name, String contact) {
        this.name = name;
        this.contact = contact;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContact() {
        return contact;
    }

    public void setContact(String contact) {
        this.contact = contact;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Retailer retailer = (Retailer) o;
        if (name != null ? !name.equals(retailer.name) : retailer.name != null) {
            return false;
        }
        return contact != null ? contact.equals(retailer.contact) : retailer.contact == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (contact != null ? contact.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Retailer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", contact='" + contact + '\'' +
                '}';
    }
}

8.3 映射 third 数据库 factory 表的领域模型类:

package tutorial.spring.boot.data.source.domain;

public class Factory {

    /**
     * 数据库表 factory 列名
     */
    public static class ColumnConstant {

        public static final String ID = "id";

        public static final String NAME = "name";

        public static final String ADDRESS = "address";
    }

    private Long id;

    private String name;

    private String address;

    public Factory(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Factory factory = (Factory) o;
        if (name != null ? !name.equals(factory.name) : factory.name != null) {
            return false;
        }
        return address != null ? address.equals(factory.address) : factory.address == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (address != null ? address.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Factory{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
  1. 编写 DAO(Data Access Object)层接口。

9.1 访问 first 数据源 customer 表接口:

package tutorial.spring.boot.data.source.dao;

import tutorial.spring.boot.data.source.domain.Customer;

import java.sql.SQLException;

public interface ICustomerDao {

    /**
     * 新增客户
     *
     * @param customer 封装客户信息的Customer对象
     * @return 插入记录主键
     */
    long insert(Customer customer) throws SQLException;

    /**
     * 根据 ID 查询客户
     *
     * @param id 客户ID,对应主键
     * @return 查询到的客户记录,如无对应记录则返回 null
     */
    Customer select(long id);
}

9.2 访问 second 数据源 retailer 表接口:

package tutorial.spring.boot.data.source.dao;

import tutorial.spring.boot.data.source.domain.Retailer;

import java.sql.SQLException;

public interface IRetailerDao {

    /**
     * 新增零售商
     *
     * @param retailer 封装零售商信息的Retailer对象
     * @return 插入记录主键
     */
    long insert(Retailer retailer) throws SQLException;

    /**
     * 根据 ID 查询零售商
     *
     * @param id 零售商ID,对应主键
     * @return 查询到的零售商记录,如无对应记录则返回 null
     */
    Retailer select(long id);
}

9.3 访问 third 数据源 factory 表接口:

package tutorial.spring.boot.data.source.dao;

import tutorial.spring.boot.data.source.domain.Factory;

import java.sql.SQLException;

public interface IFactoryDao {

    /**
     * 新增工厂
     *
     * @param factory 封装工厂信息的Factory对象
     * @return 插入记录主键
     */
    long insert(Factory factory) throws SQLException;

    /**
     * 根据 ID 查询工厂
     *
     * @param id 工厂ID,对应主键
     * @return 查询到的工厂记录,如无对应记录则返回 null
     */
    Factory select(long id);
}
  1. 编写 DAO(Data Access Object)层实现。

10.1 访问 first 数据源 customer 表接口实现:

package tutorial.spring.boot.data.source.dao.impl;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import tutorial.spring.boot.data.source.dao.ICustomerDao;
import tutorial.spring.boot.data.source.domain.Customer;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Objects;

@Repository
public class CustomerDaoImpl implements ICustomerDao {

    private final JdbcOperations jdbcOperations;

    public CustomerDaoImpl(@Qualifier("firstJdbcOperations") JdbcOperations jdbcOperations) {
        this.jdbcOperations = jdbcOperations;
    }

    @Override
    public long insert(Customer customer) throws SQLException {
        if (Objects.isNull(customer)) {
            throw new IllegalArgumentException("Param[customer] is null!");
        }
        final String sql = "INSERT INTO customer ("
                + Customer.ColumnConstant.NAME + ", "
                + Customer.ColumnConstant.PHONE_NUMBER + ") VALUES (?, ?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcOperations.update(connection -> {
            PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            preparedStatement.setString(1, Objects.isNull(customer.getName()) ? "Anonymous Customer" : customer.getName());
            preparedStatement.setString(2, Objects.isNull(customer.getPhoneNumber()) ? "" : customer.getPhoneNumber());
            return preparedStatement;
        }, keyHolder);
        if (Objects.isNull(keyHolder.getKey())) {
            throw new SQLException();
        }
        return keyHolder.getKey().longValue();
    }

    @Override
    public Customer select(long id) {
        String sql = "SELECT "
                + Customer.ColumnConstant.ID + ", "
                + Customer.ColumnConstant.NAME + ", "
                + Customer.ColumnConstant.PHONE_NUMBER
                + " FROM customer WHERE id=?";
        List customers = jdbcOperations.query(sql, this::mapResultSetToCustomer, id);
        return customers.size() == 0 ? null : customers.get(0);
    }

    private Customer mapResultSetToCustomer(ResultSet resultSet, int rowNum)
            throws SQLException {
        Customer customer = new Customer(resultSet.getString(Customer.ColumnConstant.NAME),
                resultSet.getString(Customer.ColumnConstant.PHONE_NUMBER));
        customer.setId(resultSet.getLong(Customer.ColumnConstant.ID));
        return customer;
    }
}

10.2 访问 second 数据源 retailer 表接口实现:

package tutorial.spring.boot.data.source.dao.impl;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import tutorial.spring.boot.data.source.dao.IRetailerDao;
import tutorial.spring.boot.data.source.domain.Retailer;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Objects;

@Repository
public class RetailerDaoImpl implements IRetailerDao {

    private final JdbcOperations jdbcOperations;

    public RetailerDaoImpl(@Qualifier("secondJdbcOperations") JdbcOperations jdbcOperations) {
        this.jdbcOperations = jdbcOperations;
    }

    @Override
    public long insert(Retailer retailer) throws SQLException {
        if (Objects.isNull(retailer)) {
            throw new IllegalArgumentException("Param[retailer] is null!");
        }
        final String sql = "INSERT INTO retailer ("
                + Retailer.ColumnConstant.NAME + ", "
                + Retailer.ColumnConstant.CONTACT + ") VALUES (?, ?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcOperations.update(connection -> {
            PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            preparedStatement.setString(1, Objects.isNull(retailer.getName()) ? "Anonymous Retailer" : retailer.getName());
            preparedStatement.setString(2, Objects.isNull(retailer.getContact()) ? "" : retailer.getContact());
            return preparedStatement;
        }, keyHolder);
        if (Objects.isNull(keyHolder.getKey())) {
            throw new SQLException();
        }
        return keyHolder.getKey().longValue();
    }

    @Override
    public Retailer select(long id) {
        String sql = "SELECT "
                + Retailer.ColumnConstant.ID + ", "
                + Retailer.ColumnConstant.NAME + ", "
                + Retailer.ColumnConstant.CONTACT
                + " FROM retailer WHERE id=?";
        List retailers = jdbcOperations.query(sql, this::mapResultSetToRetailer, id);
        return retailers.size() == 0 ? null : retailers.get(0);
    }

    private Retailer mapResultSetToRetailer(ResultSet resultSet, int rowNum)
            throws SQLException {
        Retailer retailer = new Retailer(resultSet.getString(Retailer.ColumnConstant.NAME),
                resultSet.getString(Retailer.ColumnConstant.CONTACT));
        retailer.setId(resultSet.getLong(Retailer.ColumnConstant.ID));
        return retailer;
    }
}

10.3 访问 third 数据源 factory 表接口实现:

package tutorial.spring.boot.data.source.dao.impl;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import tutorial.spring.boot.data.source.dao.IFactoryDao;
import tutorial.spring.boot.data.source.domain.Factory;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Objects;

@Repository
public class FactoryDaoImpl implements IFactoryDao {

    private final JdbcOperations jdbcOperations;

    public FactoryDaoImpl(@Qualifier("thirdJdbcOperations") JdbcOperations jdbcOperations) {
        this.jdbcOperations = jdbcOperations;
    }

    @Override
    public long insert(Factory factory) throws SQLException {
        if (Objects.isNull(factory)) {
            throw new IllegalArgumentException("Param[factory] is null!");
        }
        final String sql = "INSERT INTO factory ("
                + Factory.ColumnConstant.NAME + ", "
                + Factory.ColumnConstant.ADDRESS + ") VALUES (?, ?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcOperations.update(connection -> {
            PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            preparedStatement.setString(1, Objects.isNull(factory.getName()) ? "Anonymous Factory" : factory.getName());
            preparedStatement.setString(2, Objects.isNull(factory.getAddress()) ? "" : factory.getAddress());
            return preparedStatement;
        }, keyHolder);
        if (Objects.isNull(keyHolder.getKey())) {
            throw new SQLException();
        }
        return keyHolder.getKey().longValue();
    }

    @Override
    public Factory select(long id) {
        String sql = "SELECT "
                + Factory.ColumnConstant.ID + ", "
                + Factory.ColumnConstant.NAME + ", "
                + Factory.ColumnConstant.ADDRESS
                + " FROM factory WHERE id=?";
        List factories = jdbcOperations.query(sql, this::mapResultSetToFactory, id);
        return factories.size() == 0 ? null : factories.get(0);
    }

    private Factory mapResultSetToFactory(ResultSet resultSet, int rowNum)
            throws SQLException {
        Factory factory = new Factory(resultSet.getString(Factory.ColumnConstant.NAME),
                resultSet.getString(Factory.ColumnConstant.ADDRESS));
        factory.setId(resultSet.getLong(Factory.ColumnConstant.ID));
        return factory;
    }
}
  1. 编写单元测试。

11.1 测试访问 first 数据源 customer 表:

package tutorial.spring.boot.data.source.dao;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import tutorial.spring.boot.data.source.domain.Customer;

import java.sql.SQLException;

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional("firstDataSourceTransactionManager")
public class CustomerDaoTest {

    @Autowired
    private ICustomerDao customerDao;

    @Test
    public void test() throws SQLException {
        Assert.assertNotNull(customerDao);
        Customer customer = new Customer("Tommy", "214-110-256");
        long id = customerDao.insert(customer);
        Assert.assertTrue(id > 0);
        Customer result = customerDao.select(id);
        Assert.assertEquals(customer, result);
    }
}

11.2 测试访问 second 数据源 retailer 表:

package tutorial.spring.boot.data.source.dao;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import tutorial.spring.boot.data.source.domain.Retailer;

import java.sql.SQLException;

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional("secondDataSourceTransactionManager")
public class RetailerDaoTest {

    @Autowired
    private IRetailerDao retailerDao;

    @Test
    public void test() throws SQLException {
        Assert.assertNotNull(retailerDao);
        Retailer retailer = new Retailer("Costco", "210-111-111");
        long id = retailerDao.insert(retailer);
        Assert.assertTrue(id > 0);
        Retailer result = retailerDao.select(id);
        Assert.assertEquals(retailer, result);
    }
}

11.3 测试访问 third 数据源 factory 表:

package tutorial.spring.boot.data.source.dao;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import tutorial.spring.boot.data.source.domain.Factory;

import java.sql.SQLException;

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional("thirdDataSourceTransactionManager")
public class FactoryDaoTest {

    @Autowired
    private IFactoryDao factoryDao;

    @Test
    public void test() throws SQLException {
        Assert.assertNotNull(factoryDao);
        Factory factory = new Factory("Valued", "Texas");
        long id = factoryDao.insert(factory);
        Assert.assertTrue(id > 0);
        Factory result = factoryDao.select(id);
        Assert.assertEquals(factory, result);
    }
}

执行过程略。


总结

注意:

  1. 配置多个数据源时必须指定其中一个用 @Primary 注解;
  2. 不同的数据源可以指定不同的数据库类型和不同的连接池,本文只演示了连接 MySQL 数据库和使用 Spring Boot 默认使用的HikariCP 连接池;
  3. 不同的数据源需要指定不同的事务管理器 DataSourceTransactionManager,否则默认的事务管理器只对注解为 @Primary 的数据源生效。

你可能感兴趣的:(Spring Boot SQL 多数据源配置)