Spring Boot 2.x basic tutorial: MyBatis multi data source configuration

Keywords: Spring JDBC Mybatis MySQL

Two days ago, we introduced Multi data source configuration of JdbcTemplate as well as Multi data source configuration of Spring Data JPA , let's talk about how to configure the multi data source scenario when using MyBatis.

Add configuration for multiple data sources

First, in the configuration file of Spring Boot application.properties Set two database configurations you want to link, such as:

spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver

Note:

  1. When configuring multiple data sources, the difference between a single data source and a multiple data source is spring.datasource After that, set a data source name primary and secondary to distinguish different data source configurations. This prefix will be used in subsequent initialization of data sources.
  2. The configuration items of data source connection configuration 2.x and 1.x are different: 2.x uses spring.datasource.secondary . JDBC URL, while version 1.x uses spring.datasource.secondary.url. If this error occurs during configuration java.lang.IllegalArgumentException : JDBC URL is required with driverclassname., that's the problem with this configuration item.
  3. As you can see, no matter which data access framework is used, the configuration of data source is the same.

Initialize data source and MyBatis configuration

After completing the configuration information of multiple data sources, create a configuration class to load the configuration information, initialize the data sources, and initialize the MyBatis configuration to be used by each data source.

Here we continue to split the data source and framework configuration:

  1. Create a single configuration class for multiple data sources, such as the following:
@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();
    }

}

You can see as like as two peas JdbcTemplate and Spring Data JPA. Through @ ConfigurationProperties, you can know that the two data sources are loaded separately spring.datasource.primary . * and spring.datasource.secondary Configuration of. *. @The Primary annotation specifies the Primary data source, that is, when we do not specify a specific data source, we will use the real difference part of the Bean in the following JPA configuration.

  1. Create the MyBatis configuration for each of the two data sources.

JPA configuration of Primary data source:

@Configuration
@MapperScan(
        basePackages = "com.didispace.chapter39.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());
    }

}

JPA configuration of Secondary data source:

@Configuration
@MapperScan(
        basePackages = "com.didispace.chapter39.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());
    }

}

Note:

  1. The @ MapperScan annotation is used on the configuration class to specify the package path of Entity and Mapper defined under the current data source. In addition, sqlSessionFactory and sqlSessionTemplate need to be specified, which are initialized in the configuration class.
  2. In the constructor of the configuration class, the @ Qualifier annotation is used to specify which data source to use. Its name corresponds to the function name defined by the data source in the DataSourceConfiguration class.
  3. The implementation of SqlSessionFactory and SqlSessionTemplate are defined in the configuration class. Note that the specific data source used is correct (if you use the demonstration code here, you don't need to modify as long as the second step is OK).

When introducing JPA in the last article, because the method of entity and Repository definition was mentioned in the previous introduction to the use of JPA, the User and Repository definition codes were omitted. However, some readers still asked why they didn't have this. In fact, both of them were explained, and the warehouse codes were also included. I didn't avoid asking such a question again, so I'll post it here.

According to the above definition of Primary data source, in the com.didispace.chapter39.p package defines the entities and data access objects to be used by the Primary data source, such as the following:

@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();

}

According to the above definition of Secondary data source, in the com.didispace.chapter39 Under the. S package, define the entities and data access objects to be used by the Secondary data source, such as the following:

@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();
}

Test verification

After completing the above, we can write a test class to test whether the above multi data source configuration is correct. First, design a verification idea:

  1. Insert a piece of data into the Primary data source
  2. Query the data just inserted from the Primary data source. If the configuration is correct, you can find
  3. Query the data just inserted from the Secondary data source. If the configuration is correct, it should not be found
  4. Insert a piece of data into the Secondary data source
  5. Query the data just inserted from the Primary data source. If the configuration is correct, it should not be found
  6. Query the data just inserted from the Secondary data source. If the configuration is correct, you can find

The specific implementation is as follows:

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class Chapter39ApplicationTests {

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

    @Before
    public void setUp() {
        // Empty the test table to ensure the same result every time
        userMapperPrimary.deleteAll();
        userMapperSecondary.deleteAll();
    }

    @Test
    public void test() throws Exception {
        // Insert a piece of data into the Primary data source
        userMapperPrimary.insert("AAA", 20);

        // Query the data just inserted from the Primary data source. If the configuration is correct, you can find
        UserPrimary userPrimary = userMapperPrimary.findByName("AAA");
        Assert.assertEquals(20, userPrimary.getAge().intValue());

        // Query the data just inserted from the Secondary data source. If the configuration is correct, it should not be found
        UserSecondary userSecondary = userMapperSecondary.findByName("AAA");
        Assert.assertNull(userSecondary);

        // Insert a piece of data into the Secondary data source
        userMapperSecondary.insert("BBB", 20);

        // Query the data just inserted from the Primary data source. If the configuration is correct, it should not be found
        userPrimary = userMapperPrimary.findByName("BBB");
        Assert.assertNull(userPrimary);

        // Query the data just inserted from the Secondary data source. If the configuration is correct, you can find
        userSecondary = userMapperSecondary.findByName("BBB");
        Assert.assertEquals(20, userSecondary.getAge().intValue());
    }

}

Code example

For an example of this article, you can view the chapter3-9 directory in the following warehouse:

If you think this article is good, welcome Star support, your attention is the driving force I adhere to!

First of all: title , reprint please indicate the source. Welcome to my official account: program ape DD, get exclusive learning resources and push daily dry cargo. If you are interested in my other topics, go to my personal blog: didispace.com.

Posted by osti on Thu, 25 Jun 2020 19:19:18 -0700