MyBatisPlus - 5. Plugins

content

1. Paging plugin

1.1, add configuration class

1.2. Test

2, custom paging

2.1. Define methods in the mapper interface

2.2. Write SQL in the xml configuration file corresponding to mapper

2.2.1, enable type alias

2.2.2. Write SQL in the xml configuration file corresponding to mapper

2.3. Test

3. Optimistic locking

3.1. Scenario

3.2, optimistic lock and pessimistic lock

3.3. Simulation modification in the middle

3.3.1. Add table and test data

3.3.2, create entity class

3.3.3. Add mapper interface

3.3.4. Optimistic lock implementation process

3.3.5. MyBatisPlus implements optimistic locking

        3.3.5.1. Add the @Version annotation to the corresponding attribute of the entity class

        3.3.5.2. Add optimistic locking plugin to configuration class

        3.3.5.3. Test

        3.3.5.4. Optimization test: make Xiaowang modify it again after failing to modify it 


1. Paging plugin

MyBatis Plus comes with a paging plug-in, and the paging function can be realized with a simple configuration

1.1, add configuration class

@Configuration
@MapperScan("com.zyj.mybatisplus.mapper") //扫描mapper接口所在的包,也可以在对应的mapper接口添加@Mapper注解,就不用写这句
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

1.2. Test

testing method:

@SpringBootTest
public class MyBatisPlusPluginsTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testPage(){
        Page<User> page = new Page<>(2, 3);
        // SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?,?
        userMapper.selectPage(page, null);
        System.out.println("getRecords: " + page.getRecords()); // 获取此页的信息
        System.out.println("getPages: " + page.getPages()); // 获取总页数
        System.out.println("getTotal: " + page.getTotal()); // 获取总的记录数
        System.out.println("hasNext: " + page.hasNext()); // 是否有下一页
        System.out.println("hasPrevious: " + page.hasPrevious()); // 是否有上一页
    }

}

Output result:

==>  Preparing: SELECT COUNT(*) AS total FROM t_user WHERE is_deleted = 0
==> Parameters: 
<==    Columns: total
<==        Row: 6
<==      Total: 1
==>  Preparing: SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?,?
==> Parameters: 3(Long), 3(Long)
<==    Columns: id, name, age, email, is_deleted
<==        Row: 4, 小明, 21, [email protected], 0
<==        Row: 5, Billie, 24, [email protected], 0
<==        Row: 9, 小红, null, [email protected], 0
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7fd8c559]
getRecords: [User(id=4, name=小明, age=21, [email protected], isDeleted=0), User(id=5, name=Billie, age=24, [email protected], isDeleted=0), User(id=9, name=小红, age=null, [email protected], isDeleted=0)]
getPages: 2
getTotal: 6
hasNext: false
hasPrevious: true

2, custom paging

2.1. Define methods in the mapper interface

Note: If you use the paging object Page provided by MyBatis-Plus, the Page object must be located in the position of the first parameter

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

2.2. Write SQL in the xml configuration file corresponding to mapper

2.2.1, enable type alias

Enable type aliasing in application.yml

mybatis-plus:
  type-aliases-package: com.zyj.mybatisplus.pojo  #设置类型别名所对应的包

2.2.2. Write SQL in the xml configuration file corresponding to mapper

    <!-- Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age); -->
    <select id="selectPageVo" resultType="User">
        select uid, user_name, age, email from t_user where is_deleted = 0 and age > #{age}
    </select>

2.3. Test

testing method:

    @Test
    public void testPageVo(){
        Page<User> page = new Page<>(1, 3);
        userMapper.selectPageVo(page, 20);
        System.out.println("getRecords: " + page.getRecords()); // 获取此页的信息
        System.out.println("getPages: " + page.getPages()); // 获取总页数
        System.out.println("getTotal: " + page.getTotal()); // 获取总的记录数
        System.out.println("hasNext: " + page.hasNext()); // 是否有下一页
        System.out.println("hasPrevious: " + page.hasPrevious()); // 是否有上一页
    }

Output result:

==>  Preparing: SELECT COUNT(*) AS total FROM t_user WHERE is_deleted = 0 AND age > ?
==> Parameters: 20(Integer)
<==    Columns: total
<==        Row: 3
<==      Total: 1
==>  Preparing: select uid, user_name, age, email from t_user where is_deleted = 0 and age > ? LIMIT ?
==> Parameters: 20(Integer), 3(Long)
<==    Columns: uid, user_name, age, email
<==        Row: 3, Tom, 28, [email protected]
<==        Row: 4, 小明, 21, [email protected]
<==        Row: 5, Billie, 24, [email protected]
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@772861aa]
getRecords: [User(id=null, name=null, age=28, [email protected], isDeleted=null), User(id=null, name=null, age=21, [email protected], isDeleted=null), User(id=null, name=null, age=24, [email protected], isDeleted=null)]
getPages: 1
getTotal: 3
hasNext: false
hasPrevious: false

3. Optimistic locking

3.1. Scenario

A commodity, the cost price is 80 yuan, the price is 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 an hour later, the boss felt that the price of the product had increased to 150 yuan, which was too high and might affect sales. He also informed Xiao Wang that you reduced the price of the product by 30 yuan.

At this time, Xiao Li and Xiao Wang operate the commodity back-end system at the same time. When Xiao Li was operating, the system first took out the price of 100 yuan; Xiao Wang was also operating, and the price of the product he took out was also 100 yuan. Xiao Li added 50 yuan to the price and saved 100+50=150 yuan into the database; Xiao Wang reduced the product by 30 yuan and saved 100-30=70 yuan into the database. Yes, if there is no lock, Xiao Li's operation is completely covered by Xiao Wang's.

Now the commodity price is 70 yuan, 10 yuan lower than the cost price. A few minutes later, this item quickly sold more than 1,000 items, and the boss lost more than 10,000.

3.2, 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 taken out 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 the price after Xiao Li has completed the operation, and the final price will also be guaranteed to be 120 yuan.

3.3. Simulation modification in the middle

3.3.1. Add table and test data

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

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

3.3.2, create entity class

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    private Long id;
    private String name;
    private Integer price;
    private Integer version;
}

3.3.3. Add mapper interface

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

3.3.4. Optimistic lock implementation process

Add version field to database

When fetching a record, get the current version

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

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

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

3.3.5. MyBatisPlus implements optimistic locking

3.3.5.1. Add the @Version annotation to the corresponding attribute of the entity class

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {

    private Long id;

    private String name;

    private Integer price;

    @Version  // 标识该属性为乐观锁版本号的字段
    private Integer version;
}

3.3.5.2. Add optimistic locking plugin to configuration class

@Configuration
@MapperScan("com.zyj.mybatisplus.mapper") //扫描mapper接口所在的包,也可以在对应的mapper接口添加@Mapper注解,就不用写这句
public class MyBatisPlusConfig {

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

}

3.3.5.3. Test

testing method:

    @Test
    public void test01(){
        // 小李查询商品价格
        Product productLi = productMapper.selectById(1);
        System.out.println("小李查询到的价格:" + productLi.getPrice());
        // 小王查询商品价格
        Product productWang = productMapper.selectById(1);
        System.out.println("小王查询到的价格:" + productWang.getPrice());
        // 小李将商品价格增加50
        // UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
        productLi.setPrice(productLi.getPrice() + 50);
        productMapper.updateById(productLi);
        // 小王将商品价格减30
        productWang.setPrice(productWang.getPrice() - 30);
        productMapper.updateById(productWang);
        // 老板查询商品价格
        Product productBoss = productMapper.selectById(1);
        System.out.println("老板查询到的价格:" + productBoss.getPrice());
    }

Output result:

==>  Preparing: SELECT id,name,price,version FROM t_product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 100, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@41b1f51e]
小李查询到的价格:100
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a583586] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@975629453 wrapping com.mysql.cj.jdbc.ConnectionImpl@709ed6f3] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM t_product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 100, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a583586]
小王查询到的价格:100
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@64d4f7c7] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@927159199 wrapping com.mysql.cj.jdbc.ConnectionImpl@709ed6f3] will not be managed by Spring
==>  Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@64d4f7c7]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6cd3ad8a] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1964514128 wrapping com.mysql.cj.jdbc.ConnectionImpl@709ed6f3] will not be managed by Spring
==>  Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6cd3ad8a]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5f254608] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@787156891 wrapping com.mysql.cj.jdbc.ConnectionImpl@709ed6f3] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM t_product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 150, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5f254608]
老板查询到的价格:150

3.3.5.4. Optimization test: make Xiaowang modify it again after failing to modify it 

testing method:

    @Test
    public void test01(){
        // 小李查询商品价格
        Product productLi = productMapper.selectById(1);
        System.out.println("小李查询到的价格:" + productLi.getPrice());
        // 小王查询商品价格
        Product productWang = productMapper.selectById(1);
        System.out.println("小王查询到的价格:" + productWang.getPrice());
        // 小李将商品价格增加50
        // UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
        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 productBoss = productMapper.selectById(1);
        System.out.println("老板查询到的价格:" + productBoss.getPrice());
    }

Output result:

==>  Preparing: SELECT id,name,price,version FROM t_product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 100, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3dd818e8]
小李查询到的价格:100
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c26273d] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@976725249 wrapping com.mysql.cj.jdbc.ConnectionImpl@20ab3e3a] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM t_product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 100, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c26273d]
小王查询到的价格:100
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@501855493 wrapping com.mysql.cj.jdbc.ConnectionImpl@20ab3e3a] will not be managed by Spring
==>  Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@b9a77c8] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@2055276126 wrapping com.mysql.cj.jdbc.ConnectionImpl@20ab3e3a] will not be managed by Spring
==>  Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@b9a77c8]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75181b50] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@761533964 wrapping com.mysql.cj.jdbc.ConnectionImpl@20ab3e3a] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM t_product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 150, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75181b50]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2eeb0f9b] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@454841229 wrapping com.mysql.cj.jdbc.ConnectionImpl@20ab3e3a] will not be managed by Spring
==>  Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
==> Parameters: 外星人笔记本(String), 120(Integer), 2(Integer), 1(Long), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2eeb0f9b]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6325f352] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@365211514 wrapping com.mysql.cj.jdbc.ConnectionImpl@20ab3e3a] will not be managed by Spring
==>  Preparing: SELECT id,name,price,version FROM t_product WHERE id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, price, version
<==        Row: 1, 外星人笔记本, 120, 2
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6325f352]
老板查询到的价格:120

Guess you like

Origin blog.csdn.net/Mr_zhangyj/article/details/124111265