JdbcTemplate

依赖配置

  • 依赖如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
    </dependencies>

添加多数据源的配置

  • 先在Spring Boot的配置文件application.properties中设置两个你要链接的数据库配置,比如这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    spring.datasource.primary.jdbc-url=jdbc:mysql://192.168.56.101:3306/test1
    spring.datasource.primary.username=test
    spring.datasource.primary.password=123456
    spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
    # 次数据源
    spring.datasource.secondary.jdbc-url=jdbc:mysql://192.168.56.101:3306/test2
    spring.datasource.secondary.username=test
    spring.datasource.secondary.password=123456
    spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
  • 说明与注意

    1. 多数据源配置的时候,与单数据源不同点在于spring.datasource之后多设置一个数据源名称primarysecondary来区分不同的数据源配置,这个前缀将在后续初始化数据源的时候用到。
    2. 数据源连接配置2.x和1.x的配置项是有区别的:
      • 2.x使用spring.datasource.secondary.jdbc-url
      • 而1.x版本使用spring.datasource.secondary.url
      • 如果你在配置的时候发生了这个报错java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.,那么就是这个配置项的问题。

初始化数据源和JdbcTemplate

  • 完成多数据源的配置信息之后,就来创建个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的JdbcTemplate。你只需要在你的Spring Boot应用下添加下面的这个配置类即可完成!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package tk.fulsun.demo.config;

    import javax.sql.DataSource;
    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.jdbc.core.JdbcTemplate;

    /**
    * @author fulsun
    * @description: 数据源的配置信息
    * @date 6/11/2021 3:20 PM
    */
    @Configuration
    public class DataSourceConfiguration {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
    return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
    return DataSourceBuilder.create().build();
    }

    @Bean
    public JdbcTemplate primaryJdbcTemplate(
    @Qualifier("primaryDataSource") DataSource primaryDataSource) {
    return new JdbcTemplate(primaryDataSource);
    }

    @Bean
    public JdbcTemplate secondaryJdbcTemplate(
    @Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
    return new JdbcTemplate(secondaryDataSource);
    }
    }
  • 说明与注意

    1. 前两个Bean是数据源的创建,通过@ConfigurationProperties可以知道这两个数据源分别加载了spring.datasource.primary.*spring.datasource.secondary.*的配置。
    2. @Primary注解指定了主数据源,就是当我们不特别指定哪个数据源的时候,就会使用这个Bean
    3. 后两个Bean是每个数据源对应的JdbcTemplate。可以看到这两个JdbcTemplate创建的时候,分别注入了primaryDataSource数据源和secondaryDataSource数据源

测试

  • 完成了上面之后,我们就可以写个测试类来尝试一下上面的多数据源配置是否正确了,比如下面这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    package tk.fulsun.demo;

    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.jupiter.api.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.test.context.junit4.SpringRunner;

    /**
    * @author fulsun
    * @description: 主测试类
    * @date 6/11/2021 3:25 PM
    */
    @RunWith(SpringRunner.class)
    @SpringBootTest
    class ApplicationTest {

    @Autowired protected JdbcTemplate primaryJdbcTemplate;

    @Autowired protected JdbcTemplate secondaryJdbcTemplate;

    @Before
    public void setUp() {
    primaryJdbcTemplate.update("DELETE FROM user ");
    secondaryJdbcTemplate.update("DELETE FROM user ");
    }

    @Test
    public void test() throws Exception {
    // 往第一个数据源中插入 2 条数据
    primaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "aaa", 20);
    primaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "bbb", 30);

    // 往第二个数据源中插入 1 条数据,若插入的是第一个数据源,则会主键冲突报错
    secondaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "ccc", 20);

    // 查一下第一个数据源中是否有 2 条数据,验证插入是否成功
    Assert.assertEquals(
    "2", primaryJdbcTemplate.queryForObject("select count(1) from user", String.class));

    // 查一下第一个数据源中是否有 1 条数据,验证插入是否成功
    Assert.assertEquals(
    "1", secondaryJdbcTemplate.queryForObject("select count(1) from user", String.class));
    }
    }

  • 查询数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    mysql> select * from test1.user;
    +------+------+
    | name | age |
    +------+------+
    | aaa | 20 |
    | bbb | 30 |
    +------+------+
    2 rows in set (0.00 sec)

    mysql> select * from test2.user;
    +------+------+
    | name | age |
    +------+------+
    | ccc | 20 |
    +------+------+
    1 row in set (0.00 sec)

    说明

    • 有两个JdbcTemplate,为什么不用@Qualifier指定?
      • 这里顺带说个小知识点,当我们不指定的时候,会采用参数的名字来查找Bean,存在的话就注入。
    • 这两个JdbcTemplate创建的时候,我们也没指定名字,它们是如何匹配上的?
      • 这里也是一个小知识点,当我们创建Bean的时候,默认会使用方法名称来作为Bean的名称,所以这里就对应上了。

Spring Data JPA

依赖配置

  • 添加jpa的依赖

    1
    2
    3
    4
    5
    6
    7
    8
    <!--    <dependency>-->
    <!-- <groupId>org.springframework.boot</groupId>-->
    <!-- <artifactId>spring-boot-starter-jdbc</artifactId>-->
    <!-- </dependency>-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

添加多数据源的配置

  • 先在Spring Boot的配置文件application.properties中设置两个你要链接的数据库配置,比如这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    spring.datasource.primary.jdbc-url=jdbc:mysql://192.168.56.101:3306/test1
    spring.datasource.primary.username=test
    spring.datasource.primary.password=123456
    spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
    # 次数据源
    spring.datasource.secondary.jdbc-url=jdbc:mysql://192.168.56.101:3306/test2
    spring.datasource.secondary.username=test
    spring.datasource.secondary.password=123456
    spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
    # 日志打印执行的SQL
    spring.jpa.show-sql=true
    # Hibernate的DDL策略
    spring.jpa.hibernate.ddl-auto=create-drop
  • 这里除了JPA自身相关的配置之外,与JdbcTemplate配置时候的数据源配置完全是一致的

初始化数据源与JPA配置

  • 完成多数据源的配置信息之后,就来创建个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的JPA配置。

  • 由于JPA的配置要比JdbcTemplate的复杂很多,所以我们可将配置拆分一下来处理

  • 在使用JPA的时候,需要为不同的数据源创建不同的package来存放对应的Entity和Repository,以便于配置类的分区扫描

    • 类名上的注解@EnableJpaRepositories中指定Repository的所在位置
    • LocalContainerEntityManagerFactoryBean创建的时候,指定Entity所在的位置
    • 其他主要注意在互相注入时候,不同数据源不同配置的命名,基本就没有什么大问题了

配置DataSource类

  • 单独建一个多数据源的配置类,比如下面这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import javax.sql.DataSource;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Primary;

    /**
    * @author fulsun
    * @description: JPA数据源的配置类
    * @date 6/11/2021 4:08 PM
    */
    @Configuration
    public class JpaDataSourceConfiguration {
    @Primary
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
    return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
    return DataSourceBuilder.create().build();
    }
    }

  • 可以看到内容跟JdbcTemplate时候是一模一样的。通过@ConfigurationProperties可以知道这两个数据源分别加载了spring.datasource.primary.*spring.datasource.secondary.*的配置。

  • @Primary注解指定了主数据源,就是当我们不特别指定哪个数据源的时候,就会使用这个Bean真正差异部分在下面的JPA配置上。

JPA配置

  • 分别创建两个数据源的JPA配置。

  • primary数据源的JPA配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
    entityManagerFactoryRef = "entityManagerFactoryPrimary",
    transactionManagerRef = "transactionManagerPrimary",
    basePackages = {"tk.fulsun.demo.entity"}) // 设置Repository所在位置
    class PrimaryConfig {

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    @Autowired private JpaProperties jpaProperties;
    @Autowired private HibernateProperties hibernateProperties;

    private Map<String, Object> getVendorProperties() {
    return hibernateProperties.determineHibernateProperties(
    jpaProperties.getProperties(), new HibernateSettings());
    }

    @Primary
    @Bean(name = "entityManagerPrimary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
    return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
    }

    @Primary
    @Bean(name = "entityManagerFactoryPrimary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(
    EntityManagerFactoryBuilder builder) {
    return builder
    .dataSource(primaryDataSource)
    .packages("tk.fulsun.demo.entity") // 设置实体类所在位置
    .persistenceUnit("primaryPersistenceUnit")
    .properties(getVendorProperties())
    .build();
    }

    @Primary
    @Bean(name = "transactionManagerPrimary")
    public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
    return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
    }
    }
  • Secondary数据源的JPA配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
    entityManagerFactoryRef="entityManagerFactorySecondary",
    transactionManagerRef="transactionManagerSecondary",
    basePackages= { "com.didispace.chapter38.s" }) //设置Repository所在位置
    class SecondaryConfig {

    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;

    @Autowired
    private JpaProperties jpaProperties;
    @Autowired
    private HibernateProperties hibernateProperties;

    private Map<String, Object> getVendorProperties() {
    return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
    }

    @Bean(name = "entityManagerSecondary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
    return entityManagerFactorySecondary(builder).getObject().createEntityManager();
    }

    @Bean(name = "entityManagerFactorySecondary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
    return builder
    .dataSource(secondaryDataSource)
    .packages("com.didispace.chapter38.s") //设置实体类所在位置
    .persistenceUnit("secondaryPersistenceUnit")
    .properties(getVendorProperties())
    .build();
    }

    @Bean(name = "transactionManagerSecondary")
    PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
    return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
    }

    }

Repository编写

  • 编写实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package tk.fulsun.demo.dao.p;

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    /**
    * @author fulsun
    * @description: TODO
    * @date 6/11/2021 4:25 PM
    */
    @Entity
    @Data
    @NoArgsConstructor
    public class User {
    @Id @GeneratedValue private Long id;

    private String name;
    private Integer age;

    public User(String name, Integer age) {
    this.name = name;
    this.age = age;
    }
    }
  • Repository

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package tk.fulsun.demo.dao.p;

    import org.springframework.data.jpa.repository.JpaRepository;

    /**
    * @author fulsun
    * @description: UserRepository
    * @date 6/11/2021 4:26 PM
    */
    public interface UserRepository extends JpaRepository<User, Long> {}

  • secondary数据库的表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package tk.fulsun.demo.dao.s;

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    /**
    * @author fulsun
    * @description: Message
    * @date 6/11/2021 4:27 PM
    */
    @Entity
    @Data
    @NoArgsConstructor
    public class Message {

    @Id @GeneratedValue private Long id;

    private String title;
    private String message;

    public Message(String title, String message) {
    this.title = title;
    this.message = message;
    }
    }


    package tk.fulsun.demo.dao.s;

    import org.springframework.data.jpa.repository.JpaRepository;

    /**
    * @author fulsun
    * @description: MessageRepository
    * @date 6/11/2021 4:28 PM
    */
    public interface MessageRepository extends JpaRepository<Message, Long> {}

测试

  • 测试通过不同的Repository往不同的数据源插入数据,然后查询一下总数是否是对的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package tk.fulsun.demo;

    import lombok.extern.slf4j.Slf4j;
    import org.junit.Assert;
    import org.junit.jupiter.api.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 tk.fulsun.demo.dao.p.User;
    import tk.fulsun.demo.dao.p.UserRepository;
    import tk.fulsun.demo.dao.s.Message;
    import tk.fulsun.demo.dao.s.MessageRepository;

    /**
    * @author fulsun
    * @description: JPA方式多数据源测试
    * @date 6/11/2021 4:23 PM
    */
    @Slf4j
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class JPAApplicationTests {
    @Autowired private UserRepository userRepository;
    @Autowired private MessageRepository messageRepository;

    @Test
    public void test() throws Exception {
    userRepository.save(new User("aaa", 10));
    userRepository.save(new User("bbb", 20));
    userRepository.save(new User("ccc", 30));
    userRepository.save(new User("ddd", 40));
    userRepository.save(new User("eee", 50));

    Assert.assertEquals(5, userRepository.findAll().size());

    messageRepository.save(new Message("o1", "aaaaaaaaaa"));
    messageRepository.save(new Message("o2", "bbbbbbbbbb"));
    messageRepository.save(new Message("o3", "cccccccccc"));

    Assert.assertEquals(3, messageRepository.findAll().size());
    }
    }

MyBatis

依赖配置

  • 移除jpa的依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!--    <dependency>-->
    <!-- <groupId>org.springframework.boot</groupId>-->
    <!-- <artifactId>spring-boot-starter-jdbc</artifactId>-->
    <!-- </dependency>-->
    <!-- <dependency>-->
    <!-- <groupId>org.springframework.boot</groupId>-->
    <!-- <artifactId>spring-boot-starter-data-jpa</artifactId>-->
    <!-- </dependency>-->
    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
    </dependency>

添加多数据源的配置

  • 先在Spring Boot的配置文件application.properties中设置两个你要链接的数据库配置,比如这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    spring.datasource.primary.jdbc-url=jdbc:mysql://192.168.56.101:3306/test1
    spring.datasource.primary.username=test
    spring.datasource.primary.password=123456
    spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
    # 次数据源
    spring.datasource.secondary.jdbc-url=jdbc:mysql://192.168.56.101:3306/test2
    spring.datasource.secondary.username=test
    spring.datasource.secondary.password=123456
    spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver

    # 日志打印执行的SQL
    #spring.jpa.show-sql=true
    # Hibernate的DDL策略
    #spring.jpa.hibernate.ddl-auto=update

初始化数据源与MyBatis配置

  • 完成多数据源的配置信息之后,就来创建个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的MyBatis配置。

  • 这里我们继续将数据源与框架配置做拆分处理:

数据源配置类

  • 单独建一个多数据源的配置类,比如下面这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package tk.fulsun.demo.mybatis.config;

    import javax.sql.DataSource;
    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;

    /**
    * @author fulsun
    * @description: 数据源配置
    * @date 6/11/2021 4:49 PM
    */
    @Configuration
    public class DataSourceConfiguration {
    @Primary
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
    return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
    return DataSourceBuilder.create().build();
    }
    }

  • 可以看到内容跟JdbcTemplate、Spring Data JPA的时候是一模一样的。通过@ConfigurationProperties可以知道这两个数据源分别加载了spring.datasource.primary.*spring.datasource.secondary.*的配置。

  • @Primary注解指定了主数据源,就是当我们不特别指定哪个数据源的时候,就会使用这个Bean真正差异部分在下面的JPA配置上。

MyBatis配置

  • 分别创建两个数据源的MyBatis配置。

  • Primary数据源的JPA配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package tk.fulsun.demo.mybatis.config;

    import javax.sql.DataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    /**
    * @author fulsun
    * @description: MyBatis配置
    * @date 6/11/2021 4:50 PM
    */
    @Configuration
    @MapperScan(
    basePackages = "tk.fulsun.demo.mybatis.mapper.p",
    sqlSessionFactoryRef = "sqlSessionFactoryPrimary",
    sqlSessionTemplateRef = "sqlSessionTemplatePrimary")
    public class PrimaryConfig {
    private DataSource primaryDataSource;

    public PrimaryConfig(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
    this.primaryDataSource = primaryDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryPrimary() throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(primaryDataSource);
    return bean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplatePrimary() throws Exception {
    return new SqlSessionTemplate(sqlSessionFactoryPrimary());
    }
    }

  • Secondary数据源的JPA配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package tk.fulsun.demo.mybatis.config;

    import javax.sql.DataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    /**
    * @author fulsun
    * @description: MyBatis配置
    * @date 6/11/2021 4:50 PM
    */
    @Configuration
    @MapperScan(
    basePackages = "tk.fulsun.demo.mybatis.mapper.s",
    sqlSessionFactoryRef = "sqlSessionFactorySecondary",
    sqlSessionTemplateRef = "sqlSessionTemplateSecondary")
    public class SecondaryConfig {
    private DataSource secondaryDataSource;

    public SecondaryConfig(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
    this.secondaryDataSource = secondaryDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactorySecondary() throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(secondaryDataSource);
    return bean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplateSecondary() throws Exception {
    return new SqlSessionTemplate(sqlSessionFactorySecondary());
    }
    }

说明与注意

  1. 配置类上使用@MapperScan注解来指定当前数据源下定义的Entity和Mapper的包路径;另外需要指定sqlSessionFactory和sqlSessionTemplate,这两个具体实现在该配置类中类中初始化。
  2. 配置类的构造函数中,通过@Qualifier注解来指定具体要用哪个数据源,其名字对应在DataSourceConfiguration配置类中的数据源定义的函数名。
  3. 配置类中定义SqlSessionFactory和SqlSessionTemplate的实现,注意具体使用的数据源正确(

Mapper层编写

  • 根据上面Primary数据源的定义,在tk.fulsun.demo.mybatis.mapper.p包下,定义Primary数据源要用的实体和数据访问对象,比如下面这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    @Data
    @NoArgsConstructor
    public class UserPrimary {

    private Long id;

    private String name;
    private Integer age;

    public UserPrimary(String name, Integer age) {
    this.name = name;
    this.age = age;
    }
    }

    public interface UserMapperPrimary {

    @Select("SELECT * FROM USER WHERE NAME = #{name}")
    UserPrimary findByName(@Param("name") String name);

    @Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);

    @Delete("DELETE FROM USER")
    int deleteAll();

    }
  • 根据上面Secondary数据源的定义,在tk.fulsun.demo.mybatis.mapper.s包下,定义Secondary数据源要用的实体和数据访问对象,比如下面这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Data
    @NoArgsConstructor
    public class UserSecondary {

    private Long id;

    private String name;
    private Integer age;

    public UserSecondary(String name, Integer age) {
    this.name = name;
    this.age = age;
    }
    }

    public interface UserMapperSecondary {

    @Select("SELECT * FROM USER WHERE NAME = #{name}")
    UserSecondary findByName(@Param("name") String name);

    @Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);

    @Delete("DELETE FROM USER")
    int deleteAll();
    }

测试验证

  • 完成了上面之后,我们就可以写个测试类来尝试一下上面的多数据源配置是否正确了,先来设计一下验证思路:
  1. 往Primary数据源插入一条数据
  2. 从Primary数据源查询刚才插入的数据,配置正确就可以查询到
  3. 从Secondary数据源查询刚才插入的数据,配置正确应该是查询不到的
  4. 往Secondary数据源插入一条数据
  5. 从Primary数据源查询刚才插入的数据,配置正确应该是查询不到的
  6. 从Secondary数据源查询刚才插入的数据,配置正确就可以查询到
  • 具体实现如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    @Slf4j
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @Transactional
    public class Chapter39ApplicationTests {

    @Autowired
    private UserMapperPrimary userMapperPrimary;
    @Autowired
    private UserMapperSecondary userMapperSecondary;

    @Before
    public void setUp() {
    // 清空测试表,保证每次结果一样
    userMapperPrimary.deleteAll();
    userMapperSecondary.deleteAll();
    }

    @Test
    public void test() throws Exception {
    // 往Primary数据源插入一条数据
    userMapperPrimary.insert("AAA", 20);

    // 从Primary数据源查询刚才插入的数据,配置正确就可以查询到
    UserPrimary userPrimary = userMapperPrimary.findByName("AAA");
    Assert.assertEquals(20, userPrimary.getAge().intValue());

    // 从Secondary数据源查询刚才插入的数据,配置正确应该是查询不到的
    UserSecondary userSecondary = userMapperSecondary.findByName("AAA");
    Assert.assertNull(userSecondary);

    // 往Secondary数据源插入一条数据
    userMapperSecondary.insert("BBB", 20);

    // 从Primary数据源查询刚才插入的数据,配置正确应该是查询不到的
    userPrimary = userMapperPrimary.findByName("BBB");
    Assert.assertNull(userPrimary);

    // 从Secondary数据源查询刚才插入的数据,配置正确就可以查询到
    userSecondary = userMapperSecondary.findByName("BBB");
    Assert.assertEquals(20, userSecondary.getAge().intValue());
    }

    }