Welcome to my GitHub

Github.com/zq2599/blog…

Content: all original article classification summary and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc.;

About Druid multi-data sources

This article is “MyBatis primary combat” series of fourth chapter, a Springboot application at the same time the operation of two database scenarios, in ordinary times will also encounter, today to combat is through druID configuration of two data sources, let a Springboot application at the same time use these two data sources;

This section describes the basic configuration roadmap for multiple data sources

  1. First things first: Data sources are implemented through configuration classes, so remove springBoot’s auto-assembly of data sources;
  2. The first problem is to determine the relationship between the table and the data source, which is established in the SqlSessionFactory instance as follows:
    @Bean(name = "secondSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/second/**/*Mapper.xml"));
        return bean.getObject();
    }
Copy the code
  1. The second core problem is packet scanning, where the specified Mapper interface uses the specified sqlSessionTemplat. This relationship is in the SqlSessionTemplate configuration class (equivalent to the old VERSION of the XML configuration bean), as shown below:

*Mapper.xml; * mapper.xml; *Mapper interface; 5. The key steps of the entire configuration are as follows:

Practical overview

The actual combat content is as follows:

  1. There are two databases: Mybatis and Mybatis_second;
  2. Select * from mybatis where name = user; select * from mybatis_second where name = address;
  3. Create a Springboot application called DruidTwoSource, which has two controllers that can operate on the user and address tables.
  4. Write unit test cases and verify normal application functions by calling controller interface;
  5. The SpringBoot application is started. The Swagger verification function is normal.
  6. The Druid monitoring page is displayed.

Download the source code

  1. If you don’t want to code, you can download all the source code at GitHub, with the address and link information listed in the following table (github.com/zq2599/blog…
The name of the link note
Project home page Github.com/zq2599/blog… The project’s home page on GitHub
Git repository address (HTTPS) Github.com/zq2599/blog… The project source warehouse address, HTTPS protocol
Git repository address (SSH) [email protected]:zq2599/blog_demos.git The project source warehouse address, SSH protocol
  1. This Git project has multiple folders. The application of this chapter is in the Mybatis folder, as shown in the red box below:

Create databases and tables

  1. Create database mybatis (mybatis);
DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `age` int(32) NOT NULL.PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
Copy the code
  1. Create database mybatis_second; create table mybatis_second;
DROP TABLE IF EXISTS `address`;

CREATE TABLE `address` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `city` varchar(32) NOT NULL,
  `street` varchar(32) NOT NULL.PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
Copy the code

coding

  1. MyBatis Druidtwosource = DruidTwoSource = DruidTwoSource = DruidTwoSource = DruidTwoSource = DruidTwoSource = DruidTwoSource = DruidTwoSource = DruidTwoSource = DruidTwoSource = DruidTwoSource These two places are placed in their respective folders according to the database:

  1. The pom.xml for the DruidtwoSource project reads as follows:

      
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.bolingcavalry</groupId>
        <artifactId>mybatis</artifactId>
        <version>1.0 the SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <groupId>com.bolingcavalry</groupId>
    <artifactId>druidtwosource</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <name>druidtwosource</name>
    <description>Demo project for Mybatis Druid (two datasource) in Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <! -- swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
Copy the code
  1. Druid’s web-stat-filter and stat-view-servlet configuration are common:
server:
  port: 8080

spring:
  # 1. JDBC data sources
  datasource:
    druid:
      first:
        username: root
        password: 123456
        url: JDBC: mysql: / / 192.168.50.43:3306 / mybatis? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
        driver-class-name: com.mysql.cj.jdbc.Driver
        Initialize the number of connections in the pool
        initial-size: 5
        min-idle: 5
        max-active: 20
        Set the connection wait timeout
        max-wait: 60000
        Configure how often to detect idle connections that need to be closed, in milliseconds
        time-between-eviction-runs-millis: 60000
        Set the minimum time for a connection to live in the pool in milliseconds
        min-evictable-idle-time-millis: 30000
        Set the maximum number of milliseconds for a connection to live in the pool
        max-evictable-idle-time-millis: 300000
        validation-query: SELECT 1 FROM user
        test-while-idle: true
        test-on-borrow: true
        test-on-return: false
        PreparedStatement (PSCache) preparedStatement (PSCache) preparedStatement (PSCache
        pool-prepared-statements: true
        max-pool-prepared-statement-per-connection-size: 20
        # Configure the filters for monitoring statistics interception. After removing the filters, the MONITORING interface SQL cannot be counted. 'wall' is used for the firewall
        filters: stat,wall,slf4j
        filter:
          stat:
            merge-sql: true
            slow-sql-millis: 5000

      second:
        username: root
        password: 123456
        url: JDBC: mysql: / / 192.168.50.43:3306 / mybatis_second? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
        driver-class-name: com.mysql.cj.jdbc.Driver
        Initialize the number of connections in the pool
        initial-size: 5
        min-idle: 5
        max-active: 20
        Set the connection wait timeout
        max-wait: 60000
        Configure how often to detect idle connections that need to be closed, in milliseconds
        time-between-eviction-runs-millis: 60000
        Set the minimum time for a connection to live in the pool in milliseconds
        min-evictable-idle-time-millis: 30000
        Set the maximum number of milliseconds for a connection to live in the pool
        max-evictable-idle-time-millis: 300000
        validation-query: SELECT 1 FROM user
        test-while-idle: true
        test-on-borrow: true
        test-on-return: false
        PreparedStatement (PSCache) preparedStatement (PSCache) preparedStatement (PSCache
        pool-prepared-statements: true
        max-pool-prepared-statement-per-connection-size: 20
        # Configure the filters for monitoring statistics interception. After removing the filters, the MONITORING interface SQL cannot be counted. 'wall' is used for the firewall
        filters: stat,wall,slf4j
        filter:
          stat:
            merge-sql: true# # #
            slow-sql-millis: 5000

      #3. Basic monitoring configuration
      web-stat-filter:
        enabled: true
        url-pattern: / *
        # set which urls are not counted
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
        session-stat-enable: true
        session-stat-max-count: 100
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        reset-enable: true
        Set the login name and password of the monitoring page
        login-username: admin
        login-password: admin
        allow: 127.0. 01.
        # deny: 192.168.1.100

# log configuration
logging:
  level:
    root: INFO
    com:
      bolingcavalry:
        druidtwosource:
          mapper: debug
Copy the code
  1. User mapping configuration, please note the file location:

      
<! DOCTYPEmapper PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bolingcavalry.druidtwosource.mapper.first.UserMapper">

    <! -- Add a single record -->
    <insert id="insertWithFields" useGeneratedKeys="true" keyProperty="id">
        insert into user (id, name, age) values (#{id}, #{name}, #{age})
    </insert>

    <! Search by name -->
    <select id="findByName" parameterType="String" resultType="com.bolingcavalry.druidtwosource.entity.User">
        select id, name, age from user where name like concat('%', #{name}, '%')
    </select>

    <! -- Delete specified data -->
    <delete id="delete">
        delete from user where id= #{id}
    </delete>

</mapper>
Copy the code
  1. Address mapping configuration:

      
<! DOCTYPEmapper PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bolingcavalry.druidtwosource.mapper.second.AddressMapper">

    <! -- Add a single record -->
    <insert id="insertWithFields" useGeneratedKeys="true" keyProperty="id">
        insert into address (id, city, street) values (#{id}, #{city}, #{street})
    </insert>

    <! Search by name -->
    <select id="findByCityName" parameterType="String" resultType="com.bolingcavalry.druidtwosource.entity.Address">
        select id, city, street from address where city like concat('%', #{cityname}, '%')
    </select>

    <! -- Delete specified data -->
    <delete id="delete">
        delete from address where id= #{id}
    </delete>

</mapper>
Copy the code
  1. Select * from ‘user’ where ‘swagger’;
package com.bolingcavalry.druidtwosource.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@apiModel (description = "user entity class ")
public class User {

    @apiModelProperty (value = "user ID")
    private Integer id;

    @APIModelProperty (value = "username ", Required = true)
    private String name;

    @APIModelProperty (value = "user address ", Required = false)
    private Integer age;

    @Override
    public String toString(a) {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\' ' +
                ", age=" + age +
                '} '; }... Omit get and set methods}Copy the code
  1. Address table entity class:
package com.bolingcavalry.druidtwosource.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@apiModel (description = "address entity class ")
public class Address {

    @apiModelProperty (value = "address ID")
    private Integer id;

    @APIModelProperty (value = "city name ", Required = true)
    private String city;

    @APIModelProperty (value = "street name ", Required = true)
    private String street;

    @Override
    public String toString(a) {
        return "Address{" +
                "id=" + id +
                ", city='" + city + '\' ' +
                ", street='" + street + '\' ' +
                '} '; }... Omit get and set methods}Copy the code
  1. Start the class DuridTwoSourceApplication. Java, should pay attention to is out of data source and automatic assembly of transaction, because behind will perform these configuration manual coding:
package com.bolingcavalry.druidtwosource;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;

@SpringBootApplication(exclude={ DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, })
public class DuridTwoSourceApplication {

    public static void main(String[] args) { SpringApplication.run(DuridTwoSourceApplication.class, args); }}Copy the code
  1. Swagger configuration:
package com.bolingcavalry.druidtwosource;

import springfox.documentation.service.Contact;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Tag;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/ * * *@Description: Swagger configuration class *@author: willzhao E-mail: [email protected]
 * @date: 2020/8/11 when * /
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi(a) {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .tags(new Tag("UserController"."User Services"),
                        new Tag("AddressController"."Address Service"))
                .select()
                // The current package path
                .apis(RequestHandlerSelectors.basePackage("com.bolingcavalry.druidtwosource.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    // Build the details function of the API document, note which annotation here refers to
    private ApiInfo apiInfo(a) {
        return new ApiInfoBuilder()
                // Page title
                .title("MyBatis CURD operation")
                / / founder
                .contact(new Contact("Programmer Chen"."https://github.com/zq2599/blog_demos"."[email protected]"))
                / / version number
                .version("1.0")
                / / description
                .description("API description") .build(); }}Copy the code
  1. The data source configuration twodatasourceconfig.java is specified through the ConfigurationProperties annotation, and don’t forget to add the Primary annotation to the default data source:
package com.bolingcavalry.druidtwosource;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
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 javax.sql.DataSource;

/ * * *@Description: druid configuration class *@author: willzhao E-mail: [email protected]
 * @date: 2020/8/18 08:12 * /
@Configuration
public class TwoDataSourceConfig {

    @Primary
    @Bean(name = "firstDataSource")
    @ConfigurationProperties("spring.datasource.druid.first")
    public DataSource first(a) {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "secondDataSource")
    @ConfigurationProperties("spring.datasource.druid.second")
    public DataSource second(a) {
        returnDruidDataSourceBuilder.create().build(); }}Copy the code
  1. BasePackages and sqlSessionTemplateRef are the key attributes of the MapperScan annotation. They ultimately determine which Mapper interface uses which data source, and note that the Primary annotation is included:
package com.bolingcavalry.druidtwosource;

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;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/ * * *@Description: druid configuration class *@author: willzhao E-mail: [email protected]
 * @date: 2020/8/18 08:12 * /
@Configuration
@MapperScan(basePackages = "com.bolingcavalry.druidtwosource.mapper.first", sqlSessionTemplateRef = "firstSqlSessionTemplate")
public class DruidConfigFirst {

    @Bean(name = "firstSqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("firstDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/first/**/*Mapper.xml"));
        return bean.getObject();
    }

    @Bean(name = "firstTransactionManager")
    @Primary
    public DataSourceTransactionManager transactionManager(@Qualifier("firstDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "firstSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("firstSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return newSqlSessionTemplate(sqlSessionFactory); }}Copy the code
  1. Druidconfigsecond. Java (Primary);
package com.bolingcavalry.druidtwosource;

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;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/ * * *@Description: druid configuration class *@author: willzhao E-mail: [email protected]
 * @date: 2020/8/18 08:12 * /
@Configuration
@MapperScan(basePackages = "com.bolingcavalry.druidtwosource.mapper.second", sqlSessionTemplateRef = "secondSqlSessionTemplate")
public class DruidConfigSecond {

    @Bean(name = "secondSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/second/**/*Mapper.xml"));
        return bean.getObject();
    }

    @Bean(name = "secondTransactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("secondDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "secondSqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("secondSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return newSqlSessionTemplate(sqlSessionFactory); }}Copy the code
  1. The mapper interface class for the User table is simple, with only three interfaces. Note the package location:
package com.bolingcavalry.druidtwosource.mapper.first;

import com.bolingcavalry.druidtwosource.entity.User;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserMapper {

    int insertWithFields(User user);

    List<User> findByName(String name);

    int delete(int id);
}

Copy the code
  1. Address table Mapper interface class:
package com.bolingcavalry.druidtwosource.mapper.second;

import com.bolingcavalry.druidtwosource.entity.Address;
import org.springframework.stereotype.Repository;

import java.util.List;

/ * * *@Description: Interface class * of the address entity@author: willzhao E-mail: [email protected]
 * @date: 2020/8/4 8:32
 */

@Repository
public interface AddressMapper {

    int insertWithFields(Address address);

    List<Address> findByCityName(String cityName);

    int delete(int id);

}
Copy the code
  1. SQL > alter table user;
package com.bolingcavalry.druidtwosource.service;

import com.bolingcavalry.druidtwosource.entity.User;
import com.bolingcavalry.druidtwosource.mapper.first.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

public class UserService {
    @Autowired
    UserMapper userMapper;

    public User insertWithFields(User user) {
        userMapper.insertWithFields(user);
        return user;
    }

    public List<User> findByName(String name) {
        return userMapper.findByName(name);
    }

    public int delete(int id) {
        returnuserMapper.delete(id); }}Copy the code
  1. Address table service class:
package com.bolingcavalry.druidtwosource.service;

import com.bolingcavalry.druidtwosource.entity.Address;
import com.bolingcavalry.druidtwosource.entity.User;
import com.bolingcavalry.druidtwosource.mapper.first.UserMapper;
import com.bolingcavalry.druidtwosource.mapper.second.AddressMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class AddressService {

    @Autowired
    AddressMapper addressMapper;

    public Address insertWithFields(Address address) {
        addressMapper.insertWithFields(address);
        return address;
    }

    public List<Address> findByCityName(String cityName) {
        return addressMapper.findByCityName(cityName);
    }

    public int delete(int id) {
        returnaddressMapper.delete(id); }}Copy the code
  1. Alter table user controller:
package com.bolingcavalry.druidtwosource.controller;

import com.bolingcavalry.druidtwosource.entity.User;
import com.bolingcavalry.druidtwosource.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/user")
@Api(tags = {"UserController"})
public class UserController {

    @Autowired
    private UserService userService;

    @apiOperation (value =" new user record ", notes=" new user record ")
    @RequestMapping(value = "/insertwithfields",method = RequestMethod.PUT)
    public User create(@RequestBody User user) {
        return userService.insertWithFields(user);
    }

    @apioperation (value =" delete user record with specified ID ", notes=" delete user record with specified ID ")
    @APIIMPLICITParam (name = "id", value = "user ID", paramType = "path", Required = true, dataType = "Integer")
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public int delete(@PathVariable int id){
        return userService.delete(id);
    }

    @apiOperation (value =" fuzzy find all user records by name ", notes=" fuzzy find all user records by name ")
    @APIIMPLICITParam (name = "name", value = "username ", paramType = "path", Required = true, dataType = "String")
    @RequestMapping(value = "/findbyname/{name}", method = RequestMethod.GET)
    public List<User> findByName(@PathVariable("name") String name){
        returnuserService.findByName(name); }}Copy the code
  1. Address controller:
package com.bolingcavalry.druidtwosource.controller;

import com.bolingcavalry.druidtwosource.entity.Address;
import com.bolingcavalry.druidtwosource.service.AddressService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/ * * *@Description: User Web interface for table operations *@author: willzhao E-mail: [email protected]
 * @date: 2020/8/4 8:31) * /
@RestController
@RequestMapping("/address")
@Api(tags = {"AddressController"})
public class AddressController {

    @Autowired
    private AddressService addressService;


    @apiOperation (value =" add address record ", notes=" add address record ")
    @RequestMapping(value = "/insertwithfields",method = RequestMethod.PUT)
    public Address create(@RequestBody Address address) {
        return addressService.insertWithFields(address);
    }

    @apioperation (value =" delete address with specified ID ", notes=" delete address with specified ID ")
    @APIIMPLICITParam (name = "id", value = "address ID", paramType = "path", Required = true, dataType = "Integer")
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public int delete(@PathVariable int id){
        return addressService.delete(id);
    }

    @apiOperation (value =" address query based on city name ", notes=" address query based on city name ")
    @APIIMPLICITParam (name = "name", value = "city name", paramType = "path", Required = true, dataType = "String")
    @RequestMapping(value = "/findbycityname/{cityname}", method = RequestMethod.GET)
    public List<Address> findByName(@PathVariable("cityname") String cityName){
        returnaddressService.findByCityName(cityName); }}Copy the code
  • At this point, the coding is complete, and the unit test code is written;

Unit testing

  1. New configuration file application-test.yml, its content is the same except the red box in the following figure:

Mysql > select * from user;

package com.bolingcavalry.druidtwosource.controller;

import com.bolingcavalry.druidtwosource.entity.User;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import org.junit.jupiter.api.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import java.util.UUID;

import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;


/ * * *@Description: Unit test class *@author: willzhao E-mail: [email protected]
 * @date: 2020/8/9 had * /
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ActiveProfiles("test")
class UserControllerTest {

    @Autowired
    private MockMvc mvc;

    // The name field of the user table is used as the user name to ensure that the new and deleted records are the same
    static String testName;

    @BeforeAll
    static void init(a) {
        testName = UUID.randomUUID().toString().replaceAll("-"."");
    }

    @Test
    @Order(1)
    void insertWithFields(a) throws Exception {
        String jsonStr = "{\"name\": \"" + testName + "\", \"age\": 10}";

        mvc.perform(
                MockMvcRequestBuilders.put("/user/insertwithfields")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(jsonStr)
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name", is(testName)))
                .andDo(print())
                .andReturn()
                .getResponse()
                .getContentAsString();
    }

    @Test
    @Order(2)
    void findByName(a) throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/user/findbyname/"+ testName).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", hasSize(1)))
                .andDo(print());
    }


    @Test
    @Order(3)
    void delete(a) throws Exception {
        // Check the name of the file
        String responseString = mvc.perform(MockMvcRequestBuilders.get("/user/findbyname/"+ testName).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", hasSize(1)))
                .andDo(print())
                .andReturn()
                .getResponse()
                .getContentAsString();

        // Deserialize the array
        JsonArray jsonArray = JsonParser.parseString(responseString).getAsJsonArray();

        // Deserialize the user instance
        User user = new Gson().fromJson(jsonArray.get(0), User.class);

        // Perform the delete
        mvc.perform(MockMvcRequestBuilders.delete("/user/"+ user.getId()).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("1"))) .andDo(print()); }}Copy the code
  1. Unit tests for the Address table are as follows:
package com.bolingcavalry.druidtwosource.controller;

import com.bolingcavalry.druidtwosource.entity.Address;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import org.junit.jupiter.api.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import java.util.UUID;

import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ActiveProfiles("test")
class AddrestControllerTest {

    @Autowired
    private MockMvc mvc;

    // address table cityName, where UUID is used as user name to ensure that the new and deleted records are the same
    static String testCityName;

    @BeforeAll
    static void init(a) {
        testCityName = UUID.randomUUID().toString().replaceAll("-"."");
    }

    @Test
    @Order(1)
    void insertWithFields(a) throws Exception {
        String jsonStr = "{\"city\": \"" + testCityName + "\", \"street\": \"streetName\"}";

        mvc.perform(
                MockMvcRequestBuilders.put("/address/insertwithfields")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(jsonStr)
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.city", is(testCityName)))
                .andDo(print())
                .andReturn()
                .getResponse()
                .getContentAsString();
    }

    @Test
    @Order(2)
    void findByName(a) throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/address/findbycityname/"+ testCityName).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", hasSize(1)))
                .andDo(print());
    }


    @Test
    @Order(3)
    void delete(a) throws Exception {
        // Check the name of the file
        String responseString = mvc.perform(MockMvcRequestBuilders.get("/address/findbycityname/"+ testCityName).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", hasSize(1)))
                .andDo(print())
                .andReturn()
                .getResponse()
                .getContentAsString();

        // Deserialize the array
        JsonArray jsonArray = JsonParser.parseString(responseString).getAsJsonArray();

        // Deserialize the user instance
        Address address = new Gson().fromJson(jsonArray.get(0), Address.class);

        // Perform the delete
        mvc.perform(MockMvcRequestBuilders.delete("/address/"+ address.getId()).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("1"))) .andDo(print()); }}Copy the code
  • At this point, the coding is complete and you can begin validation;

Validation, unit testing

  1. The unit test operation corresponding to the user table is shown in the following figure. The three test methods add records, query records, and delete records successively:

2. AddrestControllerTest does the same as above;

Validation, swagger

  1. Browser visit: http://localhost:8080/swagger-ui.html, shows swagger page is as follows:

2. Try the new operation first:

3. The returned data is shown as follows:

MySQL database client tool mybatis. User table data, it can be seen that the service function is normal:

5. Verify other interfaces by yourself.

The Druid monitoring page is displayed

  1. Druid monitoring page address is: http://localhost:8080/druid, account password is admin:

2. After logging in to the database:

3. Two data sources can be seen on the data source page, as shown below:

  • Springboot + Mybatis + Druid multi-data source development and validation process

You are not alone, Xinchen original accompany all the way

  1. Java series
  2. Spring series
  3. The Docker series
  4. Kubernetes series
  5. Database + middleware series
  6. The conversation series

Welcome to pay attention to the public number: programmer Xin Chen

Wechat search “programmer Xin Chen”, I am Xin Chen, looking forward to enjoying the Java world with you…

Github.com/zq2599/blog…