SpringBoot integrates SSMP really super wow

1. Import starting dependencies (Web, Mybatis, Mybatis-Plus, druid, lombok)

Among them, druid can not be imported (use the hikari data source that comes with boot)

<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>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
        <!-- mybatisPlus core library -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
    </dependencies>

2.yml configuration file writing

The powerful function of boot is automatic assembly. We only need to configure the data source to carry out web development. Of course, there are some other configurations that can be managed uniformly with yml

#Let spring load the data source and connection pool
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/lyl?allowPublicKeyRetrieval=true &useSSL=false &serverTimezone=UTC
    username: root
    password: lyl
    type: com.alibaba.druid.pool.DruidDataSource
  main:
    banner-mode: off


mybatis-plus:
  configuration:
    #log
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    banner: false
    #Tell boot, the location of the mybatis configuration file
  mapper-locations: classpath:com/lyl/Dao/*.xml

Note here that we use MP (Mybatis-Plus) to open the log to facilitate printing of sql execution. The location of the mapper file here, if you want to put it under src, you need to specify the resource location in the build tag in maven

 <resource>
                    <!-- xml is placed in the java directory -->
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                    </includes>
                </resource>
                <!--Specify the location of the resource (xml is placed under resources, you don't need to specify it) -->
                <resource>
                    <directory>src/main/resources</directory>
                </resource>
   </resources>

3. Write the data layer

Design the table before writing the corresponding data layer

Table structure (id primary key, deleted tombstone, version optimistic lock)

Data (the relatively long id is generated by the snowflake algorithm, so the id needs to be received with bigint)

Once you have a table in java, you need the corresponding pojo object for mapping (here, use lombok for rapid development). Since the primary key uses the snowflake algorithm, use Long to receive it.

@Component
@Data
@TableName(value = "tb_user")
public class User implements Serializable {
    //Primary key auto-increment, using snowflake algorithm
    @TableId(type = IdType. ASSIGN_ID)
    private Long id;
    private String name;
    private Integer age;
    // Logical deletion, does not affect subsequent business
    @TableLogic(value = "0", delval = "1")
    private Integer deleted;
    //The definition version represents a lock to solve high concurrency
    @Version
    private Integer version;
}

4. SQL statement writing

With pojo objects, you can perform simple crud through MP, and write mappers to inherit general mappers. Of course, you need to use mybatis. I just performed AOP but still encountered pitfalls, and I will talk about two small details below)

@Mapper
public interface UserMapper extends BaseMapper<User> {
    
}

5. Business layer writing

The business layer calls the data layer interface (based on MP), and passes the unit test to perform sql test. Then the return value here is generally an object or a collection object returned by a query operation, and a Boolean returned by a DML operation, because it only needs to determine whether it is successful without specific data.

 @Autowired
    UserMapper userMapper;

    //Query all users
    @Override
    public List<User> findAll() {
        return userMapper. selectList(null);
    }
    //Query the specified user
    @Override
    public User findUserById(Long id) {
        return userMapper. selectById(id);
    }

    //Delete the specified user
    @Override
    public Boolean deleteById(Long id){
        return userMapper.deleteById(id)>0;
    }
    //New users
    @Override
    public Boolean insert(User user) {
        return userMapper.insert(user)>0;
    }
    //Modify user
    @Override
    public Boolean updateById(User user) {
        return userMapper. updateById(user)>0;
    }
    // pagination function
    @Override
    public Page PageAll(Integer current, Integer size) {
        Page<User> page = new Page(current,size);
        return userMapper. selectPage(page, null);
    }

6. Presentation layer

The MVC presentation layer calls the business layer interface, and tests whether the request can be sent through postman, and whether there is data in the response,

The response data here needs to be further encapsulated, because there are objects, collections, booleans, etc. in the data returned by the business layer interface, which are messy, so the front-end and back-end data protocols must be carried out. (Define a prescribed protocol to return a unified data format)

@Data
public class Accord {
    // Whether the operation was successful
    private Boolean flag;
    // data body information
    private Object data;
    // front end response information
    private String message;

    public Accord(Boolean flag){
        this.flag = flag;
    }

    // handle the query
    public Accord(Boolean flag, Object data) {
        this.flag = flag;
        this.data = data;
    }

    //Process DML statement
    public Accord(Boolean flag, String message) {
        this.flag = flag;
        this. message = message;
    }

    //Exception handler information encapsulation, the default is false, and there is no data
    public Accord(String message) {
        this.flag = false;
        this. message = message;
    }
}

6 Global Exception Handler

After the three-layer architecture is fully run through, the global exception handler needs to be written, and all exceptions need to be intercepted and processed, and added to the data protocol at the same time.

@RestControllerAdvice
public class UserExceptionAdvice {
    //Intercept all exception information, specific exception can be specified
    @ExceptionHandler
    public Accord DoException(Exception e){
        // log
        //notify operation and maintenance
        //send Message
        //send exception to console
        e.printStackTrace();
        return new Accord("Server failure, operation failed");
    }
}
@RestController
@RequestMapping("/users")
public class UserControllerByAccord {
    @Autowired
    UserService userService;
    @GetMapping
    public Accord findAll(){
        return new Accord(true, userService. findAll());
    }
    @GetMapping("{id}")
    public Accord findUserById(@PathVariable Long id){
        return new Accord(true, userService. findUserById(id));
    }
    @DeleteMapping("{id}")
    public Accord deleteById(@PathVariable Long id){
        return new Accord(userService.deleteById(id), userService.findUserByIdNoDel(id));
    }
    @PostMapping
    public Accord insert(@RequestBody User user) throws IOException{
        //simulate exception
// if (user. getName(). equals("123")) throw new IOException();
        Boolean flag = userService. insert(user);
        return new Accord(flag,flag ? "Added successfully" : "Added failed");
    }
    @PutMapping
    public Accord update(@RequestBody User user){
        return new Accord(userService. updateById(user), user);
    }
    @GetMapping("{current}/{size}")
    public Accord PageAll(@PathVariable Integer current, @PathVariable Integer size,String name) {
        System.out.println(name);
        Page<User> page = userService. PageAll(current, size, name);
        //If the current page number value is greater than the total page number value, jump to the maximum page number value
        if (current>page.getPages()){
             page = userService. PageAll((int)page. getPages(), size, name);
        }
        return new Accord(true,page);
    }
}

7. Front-end page writing

After the back-end data format is defined, it is necessary to fill in the data into the corresponding components while writing the page (the technology of page copy here is mainly vue plus elementUI), the interface is as follows:

This page has crud functions, as well as pagination query and conditional query functions. The front-end code is omitted. The main thing is to send asynchronous requests to the front-end controller through axious, and at the same time participate in the json data. Jackson will convert the json data to java and use it as a controller The parameters are used, and the result is returned after being called by the business layer and the data layer. Since the rustful style development is adopted, the response body @ResponseBody is set at the same time, and java is converted into json data, thus completing the front-end and back-end data interaction.

Here is a data parameter for pagination query to pass the front-end code.

 // list page query
                getAll() {
                    //send ajax request
                    // Jump to this method and save the parameter name when clicking on the query
                    console.log(this.pagination.name)
                    pram = "?name=" + this.pagination.name;
                    axios.get("http://localhost:8080/users/" + this.pagination.currentPage + "/"
                         + this.pagination.pageSize + pram
                    ,this.formData).then((res) => {
                        //Modify the returned data paging data
                        this.pagination.pageSize=res.data.data.size;
                        this.pagination.currentPage=res.data.data.current;
                        this.pagination.total=res.data.data.total;
                        this.dataList = res.data.data.records;
                    })
                },
                // switch page number
                handleCurrentChange(currentPage){
                  //Modify the page number value to the current page number value
                    this.pagination.currentPage=currentPage;
                  //execute query
                    this. getAll();
                },

8. Details

The basic functions are realized, but the program is caused by 2 bugs. It was also debugged for a long time at that time. Here I will share the solution process with you.

①The id generated by the snowflake algorithm cannot be queried

1. Question introduction

In the database, because I want to generate a globally unique id, I use the snowflake algorithm to generate it. Naturally, I use the Long long integer to receive this data in the pojo. When I finish writing the business layer, I test whether the snowflake id can be found. The data is too large to test

Since the other functions have been tested successfully, I put this problem on hold first, and then debugged the page. As a result, this problem came to my door again. When the function of the edit button is implemented, click the edit button and the information of the data will appear for modification. , the premise needs to be carried out first

The id query is the findUserById query and the corresponding information is filled in the edit column for modification. However, the previous id test is fine, but after the id generated by Snowflake, it is found that there is an error.

The prompt information here is realized by if judgment at the front end, and an error will be reported when the returned flag (data protocol) is false

2. Problem solving

Through the analysis here, it is found that because the id generated by the snowflake algorithm is 19 digits, while the Long type is 16 digits, the accuracy will be lost when the front end passes this id to the controller to receive it, resulting in query failure. Therefore, it is necessary to convert the Long type parameters to It is of String type, and annotations are added to the specific field, so that it will be automatically converted to String type after the parameter is passed in. problem solved

②The delete operation was successful but failed

1. The problem is introduced. In the process of page debugging, I want to delete the data of Wuge

Click to delete successfully, and the result pops up is that the deletion failed, but the data has disappeared. What a bad user experience, there is a bug specified

2. Problem solving

If the operation is successful, it means that the request is sent and the logic is executed. The rest is that there may be problems with the returned data format. Here is an analysis of the judgment mode of the pop-up window. When the operation is successfully executed and is not null, the pop-up is successful; otherwise Failed, then pop-up failure means that one of the two success conditions is false, and it can only fail when the second data data is null.

Execute the delete again, and check the response data, it is indeed null

By querying the returned data format of the controller, it is true that no object is passed in, only the flag is passed

So we add a query to this user information based on id and return it, then it will not be null

When deleting, print the information of the id found in the query, it exists, and the problem is solved