MybatisPlus-快速上手

一、什么是MybatisPlus

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

二、特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

三、支持数据库

任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。

  • MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb
  • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库

四、框架结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AAYqYRh2-1653734436948)(D:\Typora Data\MybatisPlus.assets\1653706687938.png)]

五、快速开始

创建数据库

我们创建一个mybatisplus的数据库,并插入一张user表

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);
DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');

在真实开发中,还需要添加version(乐观锁)、deleted(逻辑删除)、gmt_create、gmt_modified

新建一个springboot项目

我们新建一个springboot基本项目即可,添加上我们的web依赖、和lombok插件以及mysql依赖。

导入对应的依赖

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

        <!--mybatis——plus是自己开发的,并非开发,开源-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

说明:使用mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus!因为会有版本差异

配置application.yml

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
	#高版本兼容低版本,所以使用8的驱动即可
    driver-class-name: com.mysql.cj.jdbc.Driver
    #url,如果你是mysql8需要添加时区设置serverTimezone=GMT%2B8
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
    #数据库名字
    username: root
    #数据库密码
    password: 123456
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #    在application.yml中配置日志输出
  global-config:
    db-config:
      id-type: auto
#      table-prefix: t_user

连接数据库

这一步跟mybatis操作相同,mysql5 和mysql8驱动有所不同,8需要增加时区的配置

在IDEA中连接数据库就不做过多介绍了,在我博客关于mybatis中有详细介绍,当然,网上也有很多资料。

编写实体类

User

package com.cjh.mybatis_plus.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

编写Mapper接口

UserMapper,在mybatis中,你还需要写相应的xml,但是在MyabtisPlus中,你完全不需要,你只需要继承一个接口BaseMapper即可,至此,所有的CRUD代码已经完成。

package com.cjh.mybatis_plus.mapper;

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

//在对应的mapper上面实现基本的接口BaseMapper
@Repository //代表持久层
public interface UserMapper extends BaseMapper<User>{

}

注意:这里要添加User的泛型!

启动类添加扫描

package com.cjh.mybatis_plus;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.cjh.mybatis_plus.mapper")
public class MybatisPlusApplication {

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

}

注意:扫描路径不要写错!

测试

注意:我们在mapper中,没有写任何的CRUD代码

package com.cjh.mybatis_plus;

import com.cjh.mybatis_plus.mapper.UserMapper;
import com.cjh.mybatis_plus.pojo.User;
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
class MybatisPlusApplicationTests {

    //继承了basemapper,所有的方法都来自与父类,我们也可以添加我们自定义的方法
    @Autowired
    private UserMapper userMapper;
    @Test
    void contextLoads() {
        //参数是一个wrapper,条件构造器,这里我们先不用条件,就用null,即查询所有的用户
        //查询所有的用户
        List<User> user = userMapper.selectList(null);
        user.forEach(System.out::println);
    }

}

结果:

User(id=1, name=Jone, age=18, [email protected])
User(id=2, name=Jack, age=20, [email protected])
User(id=3, name=Tom, age=28, [email protected])
User(id=4, name=Sandy, age=21, [email protected])
User(id=5, name=Billie, age=24, [email protected])
2022-05-28 11:48:46.745  INFO 3600 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-05-28 11:48:46.755  INFO 3600 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.


全部都查出来的,我们没有编写任何的CRUD代码

https://www.shuzhiduo.com/A/kmzL4NWKzG/

BaseMapper

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

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.ExceptionUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;

public interface BaseMapper<T> extends Mapper<T> {
    
    
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteById(T entity);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> queryWrapper);

    int deleteBatchIds(@Param("coll") Collection<?> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    default T selectOne(@Param("ew") 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", new Object[0]);
            } else {
    
    
                return ts.get(0);
            }
        } else {
    
    
            return null;
        }
    }

    default boolean exists(Wrapper<T> queryWrapper) {
    
    
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0L;
    }

    Long selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    <P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);

    <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
}

六、配置日志输出

我们所有的sql现在是不可见的,我们希望知道它是怎么执行的,所以我们必须要看日志

我们在application.yml中配置日志输出

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

七、插入测试

 //测试插入
    @Test
    public void testInsert(){
    
    
        User user = new User();
        user.setName("菜菜");
        user.setAge(3);
        user.setEmail("[email protected]");
        int result = userMapper.insert(user);//帮我们自动生成id
        System.out.println(result);
    }

我们发现,它自动帮我们生成id了 1530399347068370946(Long)

Parameters: 1530399347068370946(Long), 菜菜(String), 3(Integer), 111111@qq.com(String)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@551e4c6d]
1
2022-05-28 12:03:32.910  INFO 13464 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-05-28 12:03:32.926  INFO 13464 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Process finished with exit code 0

数据库插入的id的默认值为:全局的唯一id,我们需要看它的主键生成策略

八、主键生成策略—雪花算法

默认ID_WORKED全局唯一id

对应数据库中的主键:uuid、自增id、雪花算法、redis、zookeeper

而Mybatisplus就是用了雪花算法

分布式系统唯一id生成:

https://blog.csdn.net/qq_37469055/article/details/118061067

雪花算法:

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。
其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0。
这个算法单机每秒内理论上最多可以生成1000*(2^12),也就是409.6万个ID。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FstiFPtG-1653734436950)(D:\Typora Data\MybatisPlus.assets\1653711028311.png)]

雪花算法描述:

  • 最高位是符号位,始终为0,不可用。

  • 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。

  • 10位的机器标识,10位的长度最多支持部署1024个节点。10位器标识符一般是5位IDC+5位machine编号,唯一确定一台机器。

  • 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

  • 几乎可以保证全球唯一

我们需要配置主键自增:

1、实体类字段加上@TableId(type = IdType.AUTO)

2、数据库字段一定要是自增!

九、更新操作

  @Test
    public void testUpdata(){
        User user = new User();
        user.setId(6L);
        user.setName("洋洋子");
        int i = userMapper.updateById(user);
        System.out.println(i);

    }

注意:updateById参数是一个对象!

并且,它可以通过自动拼接sql自动更新

十、自动填充—用于更新时间

创建时间、修改时间,这些操作一般都是自动化完成,我们不希望手动更新,阿里巴巴有明确的规定,所有的数据库表:gmt_create,gmt_modified,几乎所有的表都要配置上!而且需要自动化

方式一:数据库级别(工作中不允许你修改数据库)

在表中新增字段gmt_create,gmt_modified

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JtH1dyD6-1653734436951)(D:\Typora Data\MybatisPlus.assets\1653712551477.png)]

实体类添加字段

    private Date gmt_create;
    private Date gmt_modified;

方式二:代码级别

https://baomidou.com/pages/4c6bcf/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vdec3cmA-1653734436952)(D:\Typora Data\MybatisPlus.assets\1653713747522.png)]

我们恢复原始设置

实体类字段属性上需要增加注解

    //字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date gmt_create;

    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmt_modified;

编写处理器处理实体类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tzWztwmF-1653734436953)(D:\Typora Data\MybatisPlus.assets\1653721288294.png)]

MyMetaObjectHandler

package com.cjh.mybatis_plus.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

@Slf4j
@Component   //把它放入处理容器中去
public class MyMetaObjectHandler implements MetaObjectHandler {

    //插入式时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.setFieldValByName("gmt_create",new Date(),metaObject);
        this.setFieldValByName("gmt_modified",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
      this.setFieldValByName("gmt_modified",new Date(),metaObject);
    }
}

遇到的问题以及解决方法

1、找不到mapper

由于我们的myabtisplus是可以不用编写mapper以及xml的,所以配置文件也不需要相应的配置

所以,很大可能是由于依赖冲突了,记住,mybatis和mybatisplus的依赖不能同时导入,否则会出现版本问题。

<dependency>    <groupId>com.baomidou</groupId>    <artifactId>mybatis-plus-boot-starter</artifactId>    <version>3.4.2</version></dependency>

也不要使用最高版本,最高版本会有问题。

2、实体类id自增没有默认值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQG8YwBx-1653734436954)(D:\Typora Data\MybatisPlus.assets\1653720238091.png)]

这是因为我们的数据库表中的id没有设置自动自增导致的。

十一、乐观锁、悲观锁

在面试的过程中,我们经常会被问到乐观锁,对应的还有悲观锁,这个其实非常简单!

  • 乐观锁:顾名思义十分乐观,它总是认为不会出现问题,无论做什么,它都不会去上锁,如果出现问题,就再次更新值测试
  • 悲观锁:十分悲观,它认为总是会出现问题,无论做什么,都会上锁,再去操作
version、new version

乐观锁实现方式:

  • 取出记录时,获取当前 version
  • 更新时,带上这个 version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果 version 不对,就更新失败

乐观锁:先查询,获取版本号version =1,更新的时候version+1再附带进去

什么意思呢?

好比有两个人,同时执行更新操作,A比B更快完成了这个操作,那么这个时候B的version就不是1了,因为A先执行了这个操作,version=2≠1,所以B更新失败了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uG1Jv9zn-1653734436954)(D:\Typora Data\MybatisPlus.assets\1653720801742.png)]

测试

给数据库中增加version字段

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yYe2gSWH-1653734436955)(D:\Typora Data\MybatisPlus.assets\1653720934642.png)]

实体类添加对应的字段

    @Version
    private Integer version;

注册组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D9gJwyax-1653734436956)(D:\Typora Data\MybatisPlus.assets\1653721267627.png)]

MyBatisPlusConfig

package com.cjh.mybatis_plus.config;

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

//代表这是一个配置类
@Configuration
//自动管理事务
@EnableTransactionManagement
@MapperScan("com.cjh.mybatis_plus.mapper")
public class MyBatisPlusConfig {
        //注册乐观锁插件
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return interceptor;
        }
}

测试

//测试乐观锁成功
    @Test
    public void testOptionmisticLocker(){
    
    
        //查询用户信息
        User user = userMapper.selectById(1L);
        //修改用户信息
        user.setName("cjh");
        user.setEmail("[email protected]");
        //执行更新操作
        userMapper.updateById(user);
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ZIbpBev-1653734436956)(D:\Typora Data\MybatisPlus.assets\1653721609779.png)]

JDBC Connection [HikariProxyConnection@1698746141 wrapping com.mysql.cj.jdbc.ConnectionImpl@ec8f4b9] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email,gmt_create,gmt_modified,version FROM user WHERE id=?
==> Parameters: 1(Long)
<==    Columns: id, name, age, email, gmt_create, gmt_modified, version
<==        Row: 1, Jone, 18, test1@baomidou.com, 0000-00-00 00:00:00, 2022-05-28 12:33:29, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@13da7ab0]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@58fef7f7] was not registered for synchronization because synchronization is not active
2022-05-28 15:05:26.446  INFO 27992 --- [           main] c.c.m.handler.MyMetaObjectHandler        : start update fill ....
JDBC Connection [HikariProxyConnection@835070183 wrapping com.mysql.cj.jdbc.ConnectionImpl@ec8f4b9] will not be managed by Spring
==>  Preparing: UPDATE user SET name=?, age=?, email=?, gmt_modified=?, version=? WHERE id=? AND version=?
==> Parameters: cjh(String), 18(Integer), 22222222@qq.com(String), 2022-05-28 15:05:26.446(Timestamp), 2(Integer), 1(Long), 1(Integer)
<==    Updates: 1

我们发现,它首先是先查询了信息包括version信息,然后要执行更新操作的时候,把version+1一同更新上去了。

现在,我们执行失败的情况:

//测试乐观锁失败,多线程情况下
    @Test
    public void testOptionmisticLocker2(){
    
    
        //线程 1
        //查询用户信息
        User user1 = userMapper.selectById(1L);
        //修改用户信息
        user1.setName("cjh111");
        user1.setEmail("[email protected]");

        //模拟另外一个线程执行了插队操作
        //执行更新操作
        User user2 = userMapper.selectById(1L);
        //修改用户信息
        user2.setName("cjh222");
        user2.setEmail("[email protected]");
        userMapper.updateById(user2);


        userMapper.updateById(user1);//如果没有乐观锁就会覆盖插队线程的值!
    }

我们发现user1没有成功,我们也可以使用自旋锁赖重复提交,多线程一定要加锁!

十二、查询功能

 //测试查询
    @Test
    public void testSelectById(){
    
    
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }

//测试批量查询
    @Test
    public void testSelectByBatchId(){
    
    
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        users.forEach(System.out::println);
    }

  //多条件查询之一map
	@Test
    public void testSelectByBatchIds(){
    
    
        HashMap<String, Object> map = new HashMap<>();
        //自定义要查询
        map.put("name","菜菜");
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

十三、分页查询

1、原始的limit进行分页

2、pageHelper第三方插件

3、MP内置分页插件

如何使用

https://baomidou.com/pages/97710a/#%E5%B1%9E%E6%80%A7%E4%BB%8B%E7%BB%8D

配置分页插件

  //分页插件
         @Bean
    public PaginationInnerInterceptor paginationInnerInterceptor(){
    
    
            return new PaginationInnerInterceptor();
         }

测试

 @Test
    public  void testPage(){
    
    
        //查询第一页,并且当前5个数据
        Page<User> page = new Page<>(1,5);
        userMapper.selectPage(page,null);
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vYaf20bu-1653734436958)(D:\Typora Data\MybatisPlus.assets\1653723520187.png)]

十四、删除操作

删除操作跟查询的操作是一样的,这里就不讲了

我们这里讲讲逻辑删除

物理删除:从数据库中直接移除

逻辑删除:没有从数据库中移除,而是通过一个变量让他失效!(你以为你注销了就没有你的信息了吗,不,只是你看不到了而已)

逻辑删除是为了防止数据的丢失,类似于回收站!

在数据表中增加一个字段deleted

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dcyiFoq-1653734436959)(D:\Typora Data\MybatisPlus.assets\1653723974562.png)]

默认0代表没有被删除,1代表被删除

实体类中增加属性

  @TableLogic //逻辑删除注解
    private Integer deleted;

配置application.yml

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)这一句的意思就是扫描全局的逻辑删除的实体字段名,我们上面用的是 deleted,所以我们可以把flag改成deleted,这样就不用写实体类了。

测试

@Test
public void testDeleteById(){
    
        userMapper.deleteById(1L);}

你以为删除了吗,其实只是执行了更新操作,记录依旧在数据库中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fk8eIg4i-1653734436960)(D:\Typora Data\MybatisPlus.assets\1653724470010.png)]

但是查询的时候,会自动过滤被逻辑删除的对象,大大减少开发量。

十五、条件构造器Wrapper

十分重要,我们写一些复制的sql可以使用它来替代!

AbstractWrapper

allEq

eq

ne

gt

ge

lt

le

between

notBetween

like

notLike

likeLeft

likeRight

isNull

isNotNull

in

notIn

inSql

notInSql

groupBy

orderByAsc

orderByDesc

orderBy

having

func

or

and

nested

apply

last

exists

notExists

QueryWrapper

select

UpdateWrapper

set

setSql

lambda

使用 Wrapper 自定义SQL

kotlin持久化对象定义最佳实践

用注解

用XML

kotlin使用wrapper

链式调用 lambda 式

@Test
void contextLoads() {
    //查询name不为空的用户,并且邮箱不为空,年龄大于等于12岁
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name")
        .isNotNull("email")
        .ge("age", 18);
    userMapper.selectList(wrapper).forEach(System.out::println);
}

其他的操作都根据官方文档来就行

十六、代码自动生成器

MybatisX快速开发插件

#updatewrapper)

set

setSql

lambda

使用 Wrapper 自定义SQL

kotlin持久化对象定义最佳实践

用注解

用XML

kotlin使用wrapper

链式调用 lambda 式

@Test
void contextLoads() {
    //查询name不为空的用户,并且邮箱不为空,年龄大于等于12岁
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name")
        .isNotNull("email")
        .ge("age", 18);
    userMapper.selectList(wrapper).forEach(System.out::println);
}

其他的操作都根据官方文档来就行

十六、代码自动生成器

MybatisX快速开发插件

猜你喜欢

转载自blog.csdn.net/m0_46983722/article/details/125021524