MyBatis-Plus zero-based entry practice

MyBatis: MyBatis zero-based entry practice
MyBatis-Plus information:

链接:https://pan.baidu.com/s/16YWDS60ubD4MX70799TzrA?pwd=tdzy 
提取码:tdzy 

MyBatis-Plus

1. Introduction to MyBatis-Plus

1 Introduction

MyBatis-Plus (MP for short) is an enhancement tool of MyBatis. Based on MyBatis, only enhancements are made without changes, and it is born to simplify development and improve efficiency.

Vision
Our vision is to become the best partner of MyBatis, just like the 1P and 2P in Contra, and the efficiency is doubled when paired with friends.
insert image description here

2. Features

No intrusion : only enhancement and no change, introducing it will not affect existing projects, it is as smooth as silk
Low loss : Basic CURD will be automatically injected at startup, performance is basically lossless, and
powerful CRUD operations are directly object-oriented : Built-in general Mapper and general Service, only a small amount of configuration can realize most of the CRUD operations of a single table, and more powerful conditional constructors to meet various usage needs
Support Lambda form calls : Through Lambda expressions, it is convenient to write various Query conditions, no need to worry about wrongly written fields
Support automatic generation of primary keys : support up to 4 primary key strategies (including a distributed unique ID generator - Sequence), which can be freely configured to perfectly solve the primary key problem Support ActiveRecord mode:
support ActiveRecord form calls , Entity classes only need to inherit the Model class to perform powerful CRUD operations
Support custom global general operations : support global general method injection (Write once, use anywhere)
Built-in code generator : use code or Maven plug-ins to quickly generate Mapper, Model , Service, and Controller layer codes, template engines are supported, and there are many custom configurations waiting for you to use the
built-in paging plug-in : based on MyBatis physical paging, developers do not need to care about specific operations. After configuring the plug-in, writing paging is equivalent to ordinary List query
Pagination plug-in supports multiple databases: Support MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer and other database
built-in performance analysis plug-ins : can output SQL statements and their execution time, it is recommended to enable this function when developing and testing, and can quickly find out slow Query
the built-in global interception plug-in : provide intelligent analysis and blocking of delete and update operations on the whole table, and also customize the interception rules to prevent misoperation

3. Support database

Any database that can use MyBatis for CRUD and supports standard SQL, the specific support is as follows

MySQL, Oracle, DB2, H2, HSQL, SQLite, PostgreSQL, SQLServer, Phoenix, Gauss, ClickHouse, Sybase, OceanBase, Firebird, Cubrid, Goldilocks, csiidb Dameng database, Xugu database, Renda Jincang database, Nanda General (
Hua library) database, NTU General Database, Shentong Database, Hangao Database

4. Framework structure

insert image description here

5. Code and document address

Official address: http://mp.baomidou.com
Code release address:
Github: https://github.com/baomidou/mybatis-plus
Gitee: https://gitee.com/baomidou/mybatis-plus
Document release address: https://baomidou.com/pages/24112f

2. Introductory case

1. Development environment

IDE: idea 2020.3
JDK: JDK8+
Build tool: maven 3.6.1
MySQL version: MySQL 5.7
Spring Boot: 2.6.3
MyBatis-Plus: 3.5.1

2. Create database and tables

a> create table

CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

b>Add data

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

3. Create a Spring Boot project

a>Initialization project

Use Spring Initializr to quickly initialize a Spring Boot project
insert image description here
insert image description here
insert image description here
insert image description here

b>Introduce dependencies

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

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

        <!--mybatis-plus启动器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

        <!--lombok用于简化实体类开发-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
       
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

Install the lombok plugin in c>idea

insert image description here

4. Write code

a> deployment application.yml

spring:
  # 配置数据源信息
  datasource:
    # 配置数据源类型
    type: com.zaxxer.hikari.HikariDataSource
    # 配置连接数据库的各个信息
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus1?characterEncoding=utf-8&userSSL=false
    # url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root

Notice:

1. Driver class driver-class-name
spring boot 2.0 (built-in jdbc5 driver), driver class use:
driver-class-name: com.mysql.jdbc.Driver
spring boot 2.1 and above (built-in jdbc8 driver), driver class use:
driver-class-name: com.mysql.cj.jdbc.Driver
Otherwise, there will be WARN information when running the test case
2. Connection address url
MySQL5.7 version url:
jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding =utf-8&useSSL=false
MySQL8.0 version url:
jdbc:mysql://localhost:3306/mybatis_plus?
serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
Otherwise, run the test case and report the following error:
java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more

b>start class

Add the @MapperScan annotation to the Spring Boot startup class to scan the mapper package

@SpringBootApplication
@MapperScan("com/xusheng/mybatisplus/mapper")
public class MybatisplusApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(MybatisplusApplication.class, args);
    }

}

c>Add entity

@Data //lombok注解
public class User {
    
    
	private Long id;
	private String name;
	private Integer age;
	private String email;
}

The result after compiling the User class:
insert image description here

d>Add mapper

BaseMapper is a template mapper provided by MyBatis-Plus, which contains basic CRUD methods, and the generic type is the entity type of the operation

@Repository
public interface UserMapper extends BaseMapper<User> {
    
    

}

e> test

@SpringBootTest
public class MyBatisPlusTest {
    
    

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectList(){
    
    
        //通过条件构造器查询一个list集合,若没有条件,则可以设置null为参数
        //selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
        //userMapper.selectList(null).forEach(System.out::println);
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
 }

result:
insert image description here

Notice:

IDEA reports an error at userMapper because the injected object cannot be found, because the class is dynamically created, but the program can be executed correctly.
In order to avoid error reporting, you can add the @Repository annotation to the mapper interface

f> add log

Configure log output in application.yml

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  

insert image description here

3. Basic CRUD

1、BaseMapper

The basic CRUD in MyBatis-Plus has been implemented in the built-in BaseMapper, we can use it directly, the interface is as follows:

/*
 * Copyright (c) 2011-2022, baomidou ([email protected]).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import org.apache.ibatis.annotations.Param;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;



/**
 * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
 * <p>这个 Mapper 支持 id 泛型</p>
 *
 * @author hubin
 * @since 2016-01-23
 */
public interface BaseMapper<T> extends Mapper<T> {
    
    

    /**
     * 插入一条记录
     *
     * @param entity 实体对象
     */
    int insert(T entity);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    int deleteById(Serializable id);

    /**
     * 根据实体(ID)删除
     *
     * @param entity 实体对象
     * @since 3.4.4
     */
    int deleteById(T entity);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 删除(根据ID或实体 批量删除)
     *
     * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)
     */
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> idList);

    /**
     * 根据 ID 修改
     *
     * @param entity 实体对象
     */
    int updateById(@Param(Constants.ENTITY) T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象 (set 条件值,可以为 null)
     * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T selectById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,查询一条记录
     * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
    
    
        List<T> ts = this.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(ts)) {
    
    
            if (ts.size() != 1) {
    
    
                throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
            }
            return ts.get(0);
        }
        return null;
    }

    /**
     * 根据 Wrapper 条件,判断是否存在记录
     *
     * @param queryWrapper 实体对象封装操作类
     * @return
     */
    default boolean exists(Wrapper<T> queryWrapper) {
    
    
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0;
    }

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     * <p>注意: 只返回第一个字段的值</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类
     */
    <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}

2. Insert

 @Test
    public void testInsert(){
    
    
        //实现新增用户信息
        //INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
        User user = new User();
        user.setId(100L);
        user.setName("xusheng");
        user.setAge(23);
        user.setEmail("[email protected]");
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
        System.out.println("id:"+user.getId());

    }

When MyBatis-Plus inserts data, it will generate id based on the strategy of snowflake algorithm by default

3. Delete

a> Delete record by id

@Test
public void testDeleteById(){
    
    
	//通过id删除用户信息
	//DELETE FROM user WHERE id=?
	int result = userMapper.deleteById(1475754982694199298L);
	System.out.println("受影响行数:"+result);
}

b> Batch delete records by id

@Test
public void testDeleteBatchIds(){
    
    
	//通过多个id批量删除
	//DELETE FROM user WHERE id IN ( ? , ? , ? )
	List<Long> idList = Arrays.asList(1L, 2L, 3L);
	int result = userMapper.deleteBatchIds(idList);
	System.out.println("受影响行数:"+result);
}

c>Delete records through map conditions

@Test
public void testDeleteByMap(){
    
    
	//根据map集合中所设置的条件删除记录
	//DELETE FROM user WHERE name = ? AND age = ?
	Map<String, Object> map = new HashMap<>();
	map.put("age", 23);
	map.put("name", "张三");
	int result = userMapper.deleteByMap(map);
	System.out.println("受影响行数:"+result);
}

4. Modify

 @Test
    public void testUpdate(){
    
    
        //修改用户信息
        //UPDATE user SET name=?, email=? WHERE id=?
        User user = new User();
        user.setId(4L);
        user.setName("李四");
        user.setEmail("[email protected]");
        int result = userMapper.updateById(user);
        System.out.println("result:"+result);
    }

5. Query

a> Query user information according to id

@Test
public void testSelectById(){
    
    
	//根据id查询用户信息
	//SELECT id,name,age,email FROM user WHERE id=?
	User user = userMapper.selectById(4L);
	System.out.println(user);
}

b> Query multiple user information based on multiple ids

@Test
public void testSelectBatchIds(){
    
    
	//根据多个id查询多个用户信息
	//SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
	List<Long> idList = Arrays.asList(4L, 5L);
	List<User> list = userMapper.selectBatchIds(idList);
	list.forEach(System.out::println);
}

c> Query user information through map conditions

@Test
public void testSelectByMap(){
    
    
	//通过map条件查询用户信息
	//SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
	Map<String, Object> map = new HashMap<>();
	map.put("age", 22);
	map.put("name", "admin");
	List<User> list = userMapper.selectByMap(map);
	list.forEach(System.out::println);
}

d>Query all data

@Test
public void testSelectList(){
    
    
	//查询所有用户信息
	//SELECT id,name,age,email FROM user
	List<User> list = userMapper.selectList(null);
	list.forEach(System.out::println);
}

By observing the methods in BaseMapper, most methods have formal parameters of Wrapper type. This is a conditional constructor, which can set different conditions for SQL statements. If there is no condition, you can assign null to the formal parameter, that is, query ( delete/modify) all data

6. General Service

Description:
General Service CRUD encapsulates the IService interface, and further encapsulates CRUD. Use get to query a single line remove to delete a list to query a collection page. The naming method of the pagination prefix distinguishes the Mapper layer to avoid confusion. The generic T is recommended for any entity object. If there is a possibility of customizing the general Service method
, Please create your own IBaseService to inherit the base class provided by Mybatis-Plus Official
website address: https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%A3

a>IService

There is an interface IService and its implementation class ServiceImpl in MyBatis-Plus, which encapsulates common business layer logic. For details, see the source code IService and ServiceImpl

b>Create Service interface and implementation class

package com.xusheng.mybatisplus.service;

import com.xusheng.mybatisplus.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *UserService继承IService模板提供的基础功能
 * @author xusheng
 * @since 2023-04-01
 */
public interface UserService extends IService<User> {
    
    

}

package com.xusheng.mybatisplus.service.impl;

import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.mapper.UserMapper;
import com.xusheng.mybatisplus.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *ServiceImpl实现了IService,提供了IService中基础功能的实现
 * * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
 * @author xusheng
 * @since 2023-04-01
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    

}

c> Number of test query records

package com.xusheng.mybatisplus;


import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.service.UserService;
import com.xusheng.mybatisplus.service.impl.UserServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;

@SpringBootTest
public class MyBatisPlusServiceTest {
    
    

    @Autowired
    private UserService userService;

    @Test
    public void testGetCount(){
    
    
        //查询总记录数
        //SELECT COUNT( * ) FROM user
        long count = userService.count();
        System.out.println("总记录数:"+count);
    }
   
}

d> test batch insert

 @Test
    public void testInsertMore(){
    
    
        //批量添加
        // SQL长度有限制,海量数据插入单条SQL无法实行,
        // 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
        //INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? )
        ArrayList<User> list = new ArrayList<>();
        for (int i = 1; i < 10; i++) {
    
    
            User user = new User();
            user.setName("ybc"+i);
            user.setAge(20+i);
            list.add(user);
        }
        boolean b = userService.saveBatch(list);
        System.out.println(b);

    }

Four, common annotations

1. @TableName

After the above tests, when using MyBatis-Plus to implement basic CRUD, we did not specify the table to be operated, but set the generic User when the Mapper interface inherited BaseMapper, and the operated table is the user table
. In conclusion, when MyBatis-Plus determines the table to operate, it is determined by the generic type of BaseMapper, that is, the entity type, and the table name of the default operation is consistent with the class name of the entity type

a> question

If the class name of the entity class type is inconsistent with the table name of the table to be operated, what problems will occur?
We renamed the table user to t_user, and the test query function
program threw an exception, Table 'mybatis_plus.user' doesn't exist, because the current table name is t_user, and the table name of the default operation is the same as the class name of the entity type, that is, user surface
insert image description here

b> solve the problem by @TableName

Add @TableName("t_user") to the entity class type to identify the table corresponding to the entity class, and the SQL statement can be successfully executed
insert image description here

c> Solve the problem through global configuration

In the process of development, we often encounter the above problems, that is, tables corresponding to entity classes have fixed prefixes, such as t_ or tbl_
At this time, you can use the global configuration provided by MyBatis-Plus to correspond to entity classes The default prefix is ​​set for the table name, so there is no need to identify the table corresponding to the entity class through @TableName on each entity class

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_

2、@TableId

After the above tests, when MyBatis-Plus implements CRUD, it will use id as the primary key column by default, and when inserting data, it will generate id based on the snowflake algorithm strategy by default

a> question

If the entity class and table represent the primary key instead of id, but other fields, such as uid, will MyBatis-Plus automatically recognize uid as the primary key column?
The attribute id in our entity class is changed to uid, and the field id in the table is also changed to uid. The test adding function
program throws an exception. Field 'uid' doesn't have a default value, indicating that MyBatis-Plus does not use uid as primary key assignment
insert image description here

b> solve the problem by @TableId

On the uid attribute in the entity class, it is identified as the primary key by @TableId, and the SQL statement can be successfully executed
insert image description here

c> value attribute of @TableId

If the attribute corresponding to the primary key in the entity class is id, and the field representing the primary key in the table is uid, at this time, if only the annotation @TableId is added to the attribute id, an exception Unknown column 'id' in 'field list' will be thrown, that is MyBatis-Plus will still use id as the primary key of the table, and the primary key in the table is the field uid.
At this time, you need to pass the value attribute of the @TableId annotation to specify the primary key field in the table, @TableId("uid") or
@TableId (value="uid")
insert image description here

d> type attribute of @TableId

The type attribute is used to define the primary key strategy
commonly used in the primary key strategy:
insert image description here
configure the global primary key strategy:

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_
      # 设置统一的主键生成策略
      id-type: auto

e>Snowflake Algorithm

background

It is necessary to choose a suitable solution to cope with the growth of data scale, so as to cope with the gradually increasing access pressure and data volume.
The expansion methods of the database mainly include: business sub-database, master-slave replication, and database sub-table.

Database table

Distributed storage of different business data in different database servers can support businesses with millions or even tens of millions of users. However, if the business continues to develop, the single-table data of the same business will also reach the processing bottleneck of a single database server. For example, if Taobao's hundreds of millions of user data are all stored in one table on one database server, it will definitely not be able to meet the performance requirements. At this time, the single table data needs to be split.
There are two ways to split single table data: vertical table split and horizontal table split. The schematic diagram is as follows:

insert image description here
vertical table

Vertical table splitting is suitable for splitting some columns that are not commonly used and take up a lot of space in the table.
For example, for the nickname and description fields in the previous diagram, suppose we are a dating website. When users filter other users, they mainly use the two fields of age and sex for query, while the two fields of nickname and description are mainly used for display. It is generally not used in business queries. The description itself is relatively long, so we can separate these two fields into another table, so that when querying age and sex, it can bring a certain performance improvement.

level table

Horizontal table sharding is suitable for tables with a particularly large number of rows. Some companies require table sharding if the number of rows in a single table exceeds 50 million. This number can be used as a reference, but it is not an absolute standard. The key depends on the access performance of the table. For some more complex tables, it may be more than 10 million to divide the table; and for some simple tables, even if the stored data exceeds 100 million rows, it is not necessary to divide the table.
But in any case, when the data volume of the table reaches tens of millions, the architect must be vigilant, because this is likely to be a performance bottleneck or hidden danger of the architecture.
Compared with vertical table partitioning, horizontal table partitioning will introduce more complexity, such as how to deal with the requirement of globally unique data id

主键自增
①以最常见的用户 ID 为例,可以按照 1000000 的范围大小进行分段,1 ~ 999999 放到表 1中,
1000000 ~ 1999999 放到表2中,以此类推。
②复杂点:分段大小的选取。分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会
导致单表依然存在性能问题,一般建议分段大小在 100 万至 2000 万之间,具体需要根据业务选取合适
的分段大小。
③优点:可以随着数据的增加平滑地扩充新的表。例如,现在的用户是 100 万,如果增加到 1000 万,
只需要增加新的表就可以了,原有的数据不需要动。
④缺点:分布不均匀。假如按照 1000 万来进行分表,有可能某个分段实际存储的数据量只有 1 条,而
另外一个分段实际存储的数据量有 1000 万条。
取模
①同样以用户 ID 为例,假如我们一开始就规划了 10 个数据库表,可以简单地用 user_id % 10 的值来
表示数据所属的数据库表编号,ID 为 985 的用户放到编号为 5 的子表中,ID 为 10086 的用户放到编号6 的子表中。
②复杂点:初始表数量的确定。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。
③优点:表分布比较均匀。
④缺点:扩充新的表很麻烦,所有数据都要重分布。
雪花算法
雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的
主键的有序性。
①核心思想:
长度共64bit(一个long型)。
首先是一个符号位,1bit标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负
数是1,所以id一般是正数,最高位是041bit时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年。
10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID,可以部署在1024个节点)。
12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。

insert image description here
②Advantages: On the whole, they are sorted according to time increment, and ID collisions will not occur in the entire distributed system, and the efficiency is high.

3、@TableField

After the above tests, we can find that when MyBatis-Plus executes SQL statements, it must ensure that the attribute names in the entity class are consistent with the field names in the table. If the attribute names in the entity class are inconsistent with the field names, what problems will occur
? ?

a> Case 1

If the attributes in the entity class use the camel case naming style, and the fields in the table use the underscore
naming
style
Equivalent to configuring in MyBatis

b> Case 2

If the attributes in the entity class and the fields in the table do not meet condition 1,
such as the entity class attribute name, the field username in the table
needs to use @TableField("username") on the entity class attribute to set the field name corresponding to the attribute
insert image description here

4、@TableLogic

a> Tombstone

Physical deletion: real deletion, delete the corresponding data from the database, and then the deleted data cannot be queried
Logical deletion: false deletion, change the status of the field representing whether the corresponding data has been deleted to "deleted status", and then You can still see this data record in the database
Usage scenario: data recovery is possible

b> implement logical deletion

step1: Create a tombstone status column in the database and set the default value to 0
insert image description here
step2: Add the tombstone attribute to the entity class
insert image description here
step3: Test

To test the deletion function, the actual execution is to modify UPDATE t_user SET is_deleted=1 WHERE id=? AND
is_deleted=0 To test the query function, the logically deleted data will not be queried by default SELECT id,username AS
name,age,email,is_deleted FROM t_user WHERE is_deleted=0

5. Conditional constructors and commonly used interfaces

1. Introduction to wrappers

insert image description here

Wrapper : 条件构造抽象类,最顶端父类
	AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
	QueryWrapper : 查询条件封装
	UpdateWrapperUpdate 条件封装
	AbstractLambdaWrapper : 使用Lambda 语法
		LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
		LambdaUpdateWrapperLambda 更新封装Wrapper

2、QueryWrapper

a>Example 1: Assembling query conditions

package com.xusheng.mybatisplus;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;
import java.util.Map;

@SpringBootTest
public class MyBatisPlusWrapperTest {
    
    

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test01(){
    
    
        //查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息
        //SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (user_name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","a")
                    .between("age",20,30)
                    .isNotNull("email");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }
 }

b>Example 2: Assembling sorting conditions

	@Test
    public void test02(){
    
    
        //查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
        //SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,uid ASC
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("age")
                .orderByAsc("id");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

c>Example 3: Assembly delete condition

	@Test
    public void test03(){
    
    
        //删除邮箱地址为null的用户信息
        //UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNotNull("email");
        int result = userMapper.delete(queryWrapper);
        System.out.println("result:"+result);
    }

d>Example 4: Priority of conditions

	@Test
    public void test04(){
    
    
        //将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
        //UPDATE t_user SET user_name=?, email=? WHERE is_deleted=0 AND (age > ? AND user_name LIKE ? OR email IS NULL)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age",20)
                .like("name","a")
                .or()
                .isNull("email");
        User user = new User();
        user.setName("小明");
        user.setEmail("[email protected]");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("result:"+result);
    }
	@Test
    public void test05() {
    
    
        //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
        //lambda中的条件优先执行
        //UPDATE t_user SET user_name=?, email=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","a")
                .and(i->i.gt("age",20).or().isNull("email"));
        User user = new User();
        user.setName("安澜");
        user.setEmail("[email protected]");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("result:"+result);
    }

e>Example 5: Assembling the select clause

	@Test
    public void test06() {
    
    
        //查询用户的用户名、年龄、邮箱信息
        //SELECT user_name,age,email FROM t_user WHERE is_deleted=0
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("name","age","email");
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        System.out.println(maps);
    }

f>Example 6: Implement subquery

	 @Test
    public void test07() {
    
    
        //查询id小于等于100的用户信息
        //SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (uid IN (select uid from t_user where uid <= 100))
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.inSql("id","select id from t_user where id <= 100");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

3、UpdateWrapper

@Test
    public void test08() {
    
    
        //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
        //组装set子句以及修改条件
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        //lambda表达式内的逻辑优先运算
        updateWrapper.like("name","a")
                .and(i->i.gt("age","20").or().isNull("email"));
        updateWrapper.set("name","安澜").set("email","[email protected]");
        int result = userMapper.update(null, updateWrapper);
        System.out.println("result:"+result);
    }

4、condition

In the actual development process, assembling conditions is a common function, and these condition data come from user input and are optional. Therefore, when we assemble these conditions, we must first determine whether the user has selected these conditions. If so, we need to Assemble the condition, if there is no selection, it must not be assembled, so as not to affect the result of SQL execution

Idea one:

	@Test
    public void test09() {
    
    
        //SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (user_name LIKE ? AND age <= ?)
        String username = "a";
        Integer ageBegin=20;
        Integer ageEnd = 30;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if(StringUtils.isNotBlank(username)){
    
    
            //isNotBlank判断某个字符创是否不为空字符串、不为null、不为空白符
            queryWrapper.like("name",username);
        }
        if(ageBegin != null){
    
    
            queryWrapper.ge("age",ageBegin);
        }
        if(ageEnd != null){
    
    
            queryWrapper.le("age",ageEnd);
        }
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

Idea 2:

There is no problem with the above implementation scheme, but the code is more complicated. We can use the overloaded method with the condition parameter to construct the query condition and simplify the code writing

 	@Test
    public void test10(){
    
    
        String username = "a";
        Integer ageBegin= null;
        Integer ageEnd = 30;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(username),"name",username)
                .ge(ageBegin != null,"age",ageBegin)
                .le(ageEnd != null,"age",ageEnd);
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

5、LambdaQueryWrapper

	@Test
    public void test11(){
    
    
        //定义查询条件,有可能为null(用户未输入)
        String username = "a";
        Integer ageBegin= null;
        Integer ageEnd = 30;
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(username),User::getName,username)
                .ge(ageBegin != null,User::getAge,ageBegin)
                .le(ageEnd != null,User::getAge,ageEnd);
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

6、LambdaUpdateWrapper

	@Test
    public void test12() {
    
    
        //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.like(User::getName,"a")
                .and(i->i.gt(User::getAge,20).or().isNull(User::getEmail));
        updateWrapper.set(User::getName,"安澜").set(User::getEmail,"[email protected]");
        int result = userMapper.update(null, updateWrapper);
        System.out.println("result:"+result);

    }

6. Plug-ins

1. Pagination plug-in

MyBatis Plus has its own paging plug-in, and the paging function can be realized with simple configuration

a>Add configuration class

package com.xusheng.mybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
//扫描mapper接口所在的包
@MapperScan("com/xusheng/mybatisplus/mapper")
public class MyBatisPlusConfig {
    
    

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    
    
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        

        return interceptor;
    }
}

b> test

package com.xusheng.mybatisplus;


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xusheng.mybatisplus.entity.Product;
import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.mapper.ProductMapper;
import com.xusheng.mybatisplus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class MyBatisPlusPluginsTest {
    
    
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ProductMapper productMapper;

    @Test
    public void testPage(){
    
    
        //设置分页参数
        Page<User> page = new Page<>(1, 5);
        userMapper.selectPage(page, null);
        //获取分页数据
        List<User> list = page.getRecords();
        list.forEach(System.out::println);
        System.out.println("当前页:"+page.getCurrent());
        System.out.println("每页显示的条数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());

    }

}

Test result:
User(id=1, name=Jone, age=18, [email protected], isDeleted=0) User(id=2,name=Jack, age=20, [email protected] , isDeleted=0) User(id=3, name=Tom,age=28, [email protected], isDeleted=0) User(id=4, name=Sandy, age=21,email=test4@baomidou .com, isDeleted=0) User(id=5, name=Billie, age=24, email=test5@ba
omidou.com, isDeleted=0) Current page: 1 Items displayed on each page: 5 Total records: 17 Total pages: 4 Whether there is a previous page: false Whether there is a next page: true

2. xml custom pagination

a> Define the interface method in UserMapper

 /***
     * 通过年龄查询用户信息并分页
     * MyBatis-Plus所提供的分页对象,必须位于第一个参数的位置
     */
    Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);


Write SQL in b>UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xusheng.mybatisplus.mapper.UserMapper">

    <!--Map<String,Object> selectMapById(Long id);-->
    <select id="selectMapById" resultType="map">
        select id,name ,age,email from t_user where id = #{
    
    id}
    </select>
<!--
    Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
-->
    <select id="selectPageVo" resultType="User">
        select id,name ,age,email from t_user where age > #{
    
    age}
    </select>



</mapper>

c> test

	@Test
    public void testSelectPageVo(){
    
    
        //设置分页参数
        Page<User> page = new Page<>(1, 5);
        userMapper.selectPageVo(page, 20);
        //获取分页数据
        List<User> list = page.getRecords();
        list.forEach(System.out::println);
        System.out.println("当前页:"+page.getCurrent());
        System.out.println("每页显示的条数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());
    }

Result:
User(id=3, name=Tom, age=28, [email protected], isDeleted=null) User(id=4, name=Sandy, age=21, [email protected], isDeleted=null) User(id=5, name=Billie,age=24, [email protected], isDeleted=null) User(id=8, name=ybc1, age=21,email=null, isDeleted= null) User(id=9, name=ybc2, age=22, email=null, isDeleted=null) Current
page: 1 Items displayed on each page: 5 Total records: 12 Total pages: 3 Whether there is a previous page: false whether there is a next page: true

3. Optimistic lock

a> scene

A commodity has a cost price of 80 yuan and a selling price of 100 yuan. The boss first notified Xiao Li that you should increase the price of the product by 50 yuan. Xiao Li was playing a game and was delayed for an hour. Exactly one hour later, the boss felt that the price of the product had increased to 150 yuan, which was too high and might affect sales. Also inform Xiao Wang that you will reduce the price of the product by 30 yuan.
At this time, Xiao Li and Xiao Wang operate the commodity background system at the same time. When Xiao Li operated, the system first took out the product price of 100 yuan; Xiao Wang was also operating, and the price of the product taken out was also 100 yuan. Xiao Li added 50 yuan to the price, and stored 100+50=150 yuan in the database; Xiao Wang reduced the product by 30 yuan, and stored 100-30=70 yuan in the database. Yes, if there is no lock, Xiao Li's operation will be completely covered by Xiao Wang's.
Now the commodity price is 70 yuan, which is 10 yuan lower than the cost price. A few minutes later, this product quickly sold more than 1,000 items, and the boss lost more than 10,000.

b>Optimistic lock and pessimistic lock

In the above story, if it is an optimistic lock, Xiao Wang will check whether the price has been modified before saving the price. If it has been modified, the revised price will be retrieved again, 150 yuan, so that he will store 120 yuan in the database.
If it is a pessimistic lock, after Xiao Li takes out the data, Xiao Wang can only operate on the price after Xiao Li finishes the operation, and the final price will be guaranteed to be 120 yuan.

c>Simulate modification conflicts

Add commodity table to database

CREATE TABLE t_product
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
	price INT(11) DEFAULT 0 COMMENT '价格',
	VERSION INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
	PRIMARY KEY (id)
);

adding data

INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

add entity

package com.xusheng.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
    
    
    private Long id;
    private String name;
    private Integer price;
    private Integer version;
}


add mapper

package com.xusheng.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xusheng.mybatisplus.entity.Product;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductMapper extends BaseMapper<Product> {
    
    
}

test

    @Test
    public void testProduct01(){
    
    
        //小李查询商品价格
        Product productLi = productMapper.selectById(1);
        System.out.println("小李查询商品价格: "+productLi.getPrice());
        //小王查询商品价格
        Product productWang = productMapper.selectById(1);
        System.out.println("小王查询商品价格: "+productWang.getPrice());
        //小李将商品价格+50
        productLi.setPrice(productLi.getPrice()+50);
        productMapper.updateById(productLi);
        //小王将商品价格-30
        productWang.setPrice(productWang.getPrice()-30);
        productMapper.updateById(productWang);
       
        //老板查询商品价格
        Product productLaoban = productMapper.selectById(1);
        System.out.println("老板查询商品价格: "+productLaoban.getPrice());
    }

d>Optimistic lock implementation process

Add a version field to the database
to get the current version when fetching records

SELECT id,`name`,price,`version` FROM product WHERE id=1

When updating, version + 1, if the version in the where statement is incorrect, the update will fail

UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND
`version`=1

e>Mybatis-Plus implements optimistic lock

Modify entity class

package com.xusheng.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
    
    
    private Long id;
    private String name;
    private Integer price;

    @Version
    private Integer version;
}


Add optimistic lock plugin configuration

package com.xusheng.mybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
//扫描mapper接口所在的包
@MapperScan("com/xusheng/mybatisplus/mapper")
public class MyBatisPlusConfig {
    
    

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    
    
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        return interceptor;
    }
}

Test modification conflicts

小李查询商品信息:
SELECT id,name,price,version FROM t_product WHERE id=?
小王查询商品信息:
SELECT id,name,price,version FROM t_product WHERE id=?
小李修改商品价格,自动将version+1
UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
小王修改商品价格,此时version已更新,条件不成立,修改失败
UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
最终,小王修改失败,查询价格:150
SELECT id,name,price,version FROM t_product WHERE id=?

optimize process

@Test
    public void testProduct01(){
    
    
        //小李查询商品价格
        Product productLi = productMapper.selectById(1);
        System.out.println("小李查询商品价格: "+productLi.getPrice());
        //小王查询商品价格
        Product productWang = productMapper.selectById(1);
        System.out.println("小王查询商品价格: "+productWang.getPrice());
        //小李将商品价格+50
        productLi.setPrice(productLi.getPrice()+50);
        productMapper.updateById(productLi);
        //小王将商品价格-30
        productWang.setPrice(productWang.getPrice()-30);
        int result = productMapper.updateById(productWang);
        if (result == 0){
    
    
            //操作失败,重试
            Product productNew = productMapper.selectById(1);
            productNew.setPrice(productNew.getPrice()-30);
            productMapper.updateById(productNew);
        }
        //老板查询商品价格
        Product productLaoban = productMapper.selectById(1);
        System.out.println("老板查询商品价格: "+productLaoban.getPrice());
    }

Seven, general enumeration

Some field values ​​in the table are fixed, such as gender (male or female), at this time we can use the general enumeration of MyBatis-Plus to achieve

1. Add field sex to database table

insert image description here

2. Create a generic enumeration type

package com.xusheng.mybatisplus.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;

@Getter
public enum SexEnum {
    
    

    MALE(1, "男"),
    FEMALE(2, "女");

    @EnumValue //将注解所标识的属性的值存储到数据库中
    private Integer sex;
    private String sexName;

     SexEnum(Integer sex, String sexName) {
    
    
        this.sex = sex;
        this.sexName = sexName;
    }
}

3. Configuration scan general enumeration

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_
      # 设置统一的主键生成策略
      id-type: auto
  # 配置类型别名所对应的包
  type-aliases-package: com.xusheng.mybatisplus.entity
  # 扫描通用枚举的包
  type-enums-package: com.xusheng.mybatisplus.enums

4. Test

package com.xusheng.mybatisplus;


import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.enums.SexEnum;
import com.xusheng.mybatisplus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MyBatisPlusEnumTest {
    
    

    @Autowired
    private UserMapper userMapper;

    @Test
    public  void test(){
    
    
        User user = new User();
        user.setName("admin");
        user.setAge(33);
        //设置性别信息为枚举项,会将@EnumValue注解所标识的属性值存储到数据库
        user.setSex(SexEnum.MALE);
        //INSERT INTO t_user ( username, age, sex ) VALUES ( ?, ?, ? )
        //Parameters: Enum(String), 20(Integer), 1(Integer)
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
    }
}

8. Code generator

1. Introduce dependencies

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

2. Rapid generation

package com.xusheng.mybatisplus;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Collections;

/**
 * Date:2022/2/15
 * Author:xusheng
 * Description:
 */
public class FastAutoGeneratorTest {
    
    

    public static void main(String[] args) {
    
    
        FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false", "root", "root")
                .globalConfig(builder -> {
    
    
                    builder.author("xusheng") // 设置作者
                            //.enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("D://mybatis_plus"); // 指定输出目录
                })
                .packageConfig(builder -> {
    
    
                    builder.parent("com.xusheng") // 设置父包名
                            .moduleName("mybatisplus") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
    
    
                    builder.addInclude("user") // 设置需要生成的表名
                            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }
/*<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>*/
}


Nine, multiple data sources

Applicable to a variety of scenarios: pure multi-library, read-write separation, one master and multiple slaves, mixed mode, etc.
Now we will simulate a pure multi-library scenario, and other scenarios are similar.
Scenario description:
We create two libraries, respectively: mybatis_plus (the previous library does not move) and mybatis_plus_1 (new), move the product table of the mybatis_plus library to the mybatis_plus_1 library, so that each library has one table, and
obtain user data and product data through a test case. If you get more instructions Library simulation succeeded

1. Create database and tables

Create database mybatis_plus_1 and table product

CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus_1`;
CREATE TABLE product
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
	price INT(11) DEFAULT 0 COMMENT '价格',
	version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
	PRIMARY KEY (id)
);

Add test data

INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

Delete the mybatis_plus library product table

use mybatis_plus;
DROP TABLE IF EXISTS product;

2. Introduce dependencies

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.5.0</version>
        </dependency>

3. Configure multiple data sources

Description: Comment out the previous database connection and add a new configuration

spring:
  # 配置数据源信息
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus1?characterEncoding=utf-8&useSSL=false
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: root
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&useSSL=false
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: root

4. Create user service

package com.xusheng.mybatisplus.service;

import com.xusheng.mybatisplus.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author xusheng
 * @since 2023-04-04
 */
public interface UserService extends IService<User> {
    
    

}

package com.xusheng.mybatisplus.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.mapper.UserMapper;
import com.xusheng.mybatisplus.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xusheng
 * @since 2023-04-04
 */
@Service
@DS("master")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    

}

5. Create commodity service

package com.xusheng.mybatisplus.service;

import com.xusheng.mybatisplus.entity.Product;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author xusheng
 * @since 2023-04-04
 */
public interface ProductService extends IService<Product> {
    
    

}

package com.xusheng.mybatisplus.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.xusheng.mybatisplus.entity.Product;
import com.xusheng.mybatisplus.mapper.ProductMapper;
import com.xusheng.mybatisplus.service.ProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xusheng
 * @since 2023-04-04
 */
@Service
@DS("slave_1")
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
    
    

}

6. Test

package com.xusheng.mybatisplus;


import com.xusheng.mybatisplus.service.ProductService;
import com.xusheng.mybatisplus.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MybatisPlusDatasourceApplicationTests {
    
    

    @Test
    void contextLoads() {
    
    
    }

    @Autowired
    private UserService userService;

    @Autowired
    private ProductService productService;

    @Test
    public void test(){
    
    
        System.out.println(userService.getById(1));
        System.out.println(productService.getById(1));
    }

}

Results:
1. If the object can be successfully obtained, the test is successful.
2. If we realize the separation of reading and writing, add the write operation method to the data source of the master database, and the read operation method to add the data source of the slave database, and automatically switch, is it possible? Achieving read-write separation?

10. MyBatisX plugin

MyBatis-Plus provides us with powerful mapper and service templates, which can greatly improve development efficiency;
but in the real development process, MyBatis-Plus cannot solve all problems for us, such as some complex SQL, multi-table joint query, We need to write code and SQL statements by ourselves. How can we quickly solve this problem? At this time, we can use the MyBatisX plug-in;
MyBatisX is a rapid development plug-in based on IDEA, born for efficiency.

MyBatisX plugin usage: https://baomidou.com/pages/ba5b24/

Guess you like

Origin blog.csdn.net/m0_52435951/article/details/129968371