MyBatisPlus笔记(高级)

作者:故事我忘了
个人微信公众号:程序猿的月光宝盒
MyBatisPlus笔记(高级)

说明:

本文是继上一篇入门的进阶版

相关连接:

慕课入门视频:

https://www.imooc.com/learn/1130

入门文章:

https://www.cnblogs.com/jsccc520/p/14669347.html

本文对应进阶视频:

https://www.imooc.com/learn/1171

整合的github地址:

https://github.com/monkeyKinn/StudyMyBatisPlus

觉得不错给个star呗~

star

star

star

Start~

数据准备

#创建用户表
CREATE TABLE user_high (
    id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
    name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
    age INT(11) DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
    manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
    create_time DATETIME DEFAULT NULL COMMENT '创建时间',
	update_time DATETIME DEFAULT NULL COMMENT '修改时间',
	version INT(11) DEFAULT '1' COMMENT '版本',
	deleted INT(1) DEFAULT '0' COMMENT '逻辑删除标识(0.未删除,1.已删除)',
    CONSTRAINT manager_fk FOREIGN KEY (manager_id)
        REFERENCES user_high (id)
)  ENGINE=INNODB CHARSET=UTF8;

#初始化数据:
INSERT INTO user_high (id, name, age, email, manager_id
	, create_time)
VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL
		, '2019-01-11 14:20:20'),
	(1088248166370832385, '王天风', 25, 'wtf@baomidou.com', 1087982257332887553
		, '2019-02-05 11:12:22'),
	(1088250446457389058, '李艺伟', 28, 'lyw@baomidou.com', 1088248166370832385
		, '2019-02-14 08:31:16'),
	(1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385
		, '2019-01-14 09:15:15'),
	(1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385
		, '2019-01-14 09:48:16');

注意在user实体类中加上注解映射表名

@TableName("user_high")

因为项目直接是分支下来的 配置就不配置了

具体的到我github上看吧 有需要的话

逻辑删除

简介

加个字段,表示删除状态----软删除了,有选项是级联删除....这就约等于删库跑路,,老板疯狂追你三条gai

MP实现

首先配置文件中给配置上

spring.application.name=MyBatisPlus
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mp?useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=admin


#日志输出配置
logging.level.root=warn
#只想看这个包里的sql日志 trace是最低级别
logging.level.com.jsc.mybatisplus.mapper=trace
# p:级别,m:内容,n:换行
logging.pattern.console=%p%m%n

#配置全局逻辑删除
#没删除 标志0
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#删除 标志1
mybatis-plus.global-config.db-config.logic-delete-value=1

配置类

/**
 * mybatis的配置类
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 * @date Created in 2021/04/16 15:48
 */
@Configuration
public class MyBatisPlusConfig {

    // MP 3.1.1开始   不需要配置
    // @Bean
    // public ISqlInjector sqlInjector() {
    //     return new LogicSqlInjector();
    // }
}


在实体类中加上逻辑删除的注解@TableLogic

	@TableLogic
    private Integer deleted;

创建测试类

    @Autowired
    UserMapper userMapper;

    @Test
    void delByIdLogic() {
        // 加了前面的配置后这里就是逻辑删除了
        int i = userMapper.deleteById(1087982257332887553L);
        System.out.println("影响行数: " + i);
    }

当逻辑删除后,以后进行的查询,更新等操作都会去除已经逻辑删除的数据

	@Test
    void selectAll() {
        // 查询全部,这时候把deleted为1的过滤了
        userMapper.selectList(null).forEach(System.out::println);
    }

    @Test
    void updateById() {
        User user = new User();
        // 更新我逻辑删掉的数据,英雄行数为0 更新失败
        // user.setId(1087982257332887553L);
        // 更新没删除的别人,就可以
        user.setId(1094592041087729666L);
        user.setAge(18);
        int i = userMapper.updateById(user);
        System.out.println("影响行数: " + i);
    }

查询中排除逻辑删除字段以及注意事项

在实体类的对应字段上加上注解@TableField(select=false)

/** 逻辑删除标识(0.未删除,1.已删除) */
    @TableLogic
    @TableField(select=false)
    private Integer deleted;

下次再查询的时候这个字段就不会出现

但是在自定义的sql中,逻辑删除的还是会查出来,得看你自己的sql了

public interface UserMapper extends BaseMapper<User> {
    @Select("select * from user_high ${ew.customSqlSegment}")
    List<User> mySelectList(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}

    @Test
    void selectMyOwn() {
        // 查询全部,这时候把deleted为1的过滤了
        userMapper.mySelectList(
                Wrappers.<User>lambdaQuery()
                        .gt(User::getAge,18)
        )
                .forEach(System.out::println);
    }

User(id=1087982257332887553, name=大boss, age=40, email=boss@baomidou.com, managerId=null, createTime=2019-01-11T14:20:20, updateTime=null, version=1, deleted=1)
User(id=1088248166370832385, name=王天风, age=25, email=wtf@baomidou.com, managerId=1087982257332887553, createTime=2019-02-05T11:12:22, updateTime=null, version=1, deleted=0)
User(id=1088250446457389058, name=李艺伟, age=28, email=lyw@baomidou.com, managerId=1088248166370832385, createTime=2019-02-14T08:31:16, updateTime=null, version=1, deleted=0)
User(id=1094590409767661570, name=张雨琪, age=31, email=zjq@baomidou.com, managerId=1088248166370832385, createTime=2019-01-14T09:15:15, updateTime=null, version=1, deleted=0)

但是你想过滤的话

    @Test
    void selectMyOwn() {
        // 查询全部,这时候把deleted为1的过滤了
        userMapper.mySelectList(
                Wrappers.<User>lambdaQuery()
                        .gt(User::getAge,18)
                        // 加上就过滤了
                        .eq(User::getDeleted,0)
        )
                .forEach(System.out::println);
    }

自动填充

简介

有的项目有新增时间啊 修改时间啊 修改人啊啥的,每次都重复插入有点麻烦,虽然有些也是一个函数的事,,哎,~~~~但我就是玩儿~(不是,我就是懒

而且要是我就是要记录 是谁 在什么时间点动了数据库,这就玩(懒)不起来了..

还好,MP够懂Coder,你懒归你懒,清风拂山岗

实现

1.修改实体类

/** 创建时间 ,在插入时候填充*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/** 修改时间 ,在更新时候填充*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;

2.新建处理器类

/**
 * 我的元数据处理器
 *
 * @author 金聖聰
 * @version v1.0
 * @email jinshengcong@163.com
 * @date Created in 2021/04/17 17:00
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 插入的时候的填充方法
     *
     * @param metaObject 元数据
     * @return void 空
     * @author 金聖聰
     * @email jinshengcong@163.com
     * Modification History:
     * Date         Author        Description        version
     *--------------------------------------------------------*
     * 2021/04/17 17:02    金聖聰     修改原因            1.0
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        // 3.3.0过期了 替代方法是 strictInsertFill
        // setInsertFieldValByName("createTime", LocalDateTime.now(),metaObject);
        // 等效于 
        // setFieldValByName("createTime", LocalDateTime.now(),metaObject);
        strictInsertFill(metaObject,"createTime",LocalDateTime.class,LocalDateTime.now());
    }

    /**
     * 更新时候的填充方法
     * @param metaObject 元数据
     * @return void 空
     * @author 金聖聰
     * @email jinshengcong@163.com
     * Modification History:
     * Date         Author        Description        version
     *--------------------------------------------------------*
     * 2021/04/17 17:02    金聖聰     修改原因            1.0
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        // 3.3.0过期了 替代方法是 strictUpdateFill
        // setUpdateFieldValByName("updateTime",LocalDateTime.now(),metaObject);
        // 等效于 
        // setFieldValByName("updateTime",LocalDateTime.now(),metaObject);
        strictUpdateFill(metaObject, "updateTime", LocalDateTime.class,LocalDateTime.now());
        
    }
}

3.测试类

    @Test
    void insertAutoFilled() {
        User user = new User();
        user.setName("小李");
        user.setAge(18);
        // 自动填充了创建时间
        int insert = userMapper.insert(user);
        System.out.println("影响行数: " + insert);
    }

    @Test
    void updateAutoFilled() {
        User user = new User();
        user.setId(1383349447410843650L);
        user.setName("夹心");
        user.setAge(16);
        // 自动填充了更新时间
        int insert = userMapper.updateById(user);
        System.out.println("影响行数: " + insert);
    }

自动填充优化

在处理器类中进行优化

    @Override
    public void insertFill(MetaObject metaObject) {
        // 3.3.0过期了 替代方法是 strictInsertFill
        // setInsertFieldValByName("createTime", LocalDateTime.now(),metaObject);
        // 等效于
        // setFieldValByName("createTime", LocalDateTime.now(),metaObject);

        // 优化: 有没有这个属性,有的话才自动填充
        boolean createTime = metaObject.hasSetter("createTime");
        if (createTime) {
            strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        }
    }

当我更新的时候有设置好值,你就别帮我跟新,这么怎么设置呢

    @Override
    public void updateFill(MetaObject metaObject) {
        // 3.3.0过期了 替代方法是 strictUpdateFill
        // setUpdateFieldValByName("updateTime",LocalDateTime.now(),metaObject);
        // 等效于
        // setFieldValByName("updateTime",LocalDateTime.now(),metaObject);

        // 优化:  只有这个为null的时候才进行自动填充
        Object updateTime = getFieldValByName("updateTime", metaObject);
        if (ObjectUtils.isEmpty(updateTime)) {
            strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        }

    }

测试类

    @Test
    void updateById() {
        User user = new User();
        // 更新我逻辑删掉的数据,英雄行数为0 更新失败
        // user.setId(1087982257332887553L);
        // 更新没删除的别人,就可以
        user.setId(1383353200797118465L);
        user.setAge(10);
        // user.setUpdateTime(LocalDateTime.now().plusDays(1));
        int i = userMapper.updateById(user);
        System.out.println("影响行数: " + i);
    }

乐观锁插件

简介

意图: 当要更新一条记录的时候,希望这条数据没有被别人更新过,是为了防止更新冲突的问题

应用场景:

一般是悲观锁和乐观锁

悲观锁是通过数据库的锁机制实现 ----多写,少读

乐观锁是通过表的版本号实现 ---写比较少的场景,多读的场景

实现原理

1.版本号

a) 取出记录时,获取当前的version

b) 更新这条记录时,带上这个version

c) 版本正确,更新成功,版本错误,更新失败

上一篇:MyBatisPlus分页查询


下一篇:redission实现分布式锁(文字少,都是代码)