• w3cschoolMyBatisPlus 插件


    https://www.w3cschool.cn/mybatis_plus/mybatis_plus-udwn3mgc.html

    MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 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 ​的数据库,具体支持情况如下:

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

    MyBatis-Plus 快速入门-快速开始

    2022-03-23 16:02 更新

    我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,我们假设您已经:

    • 拥有 ​Java ​开发环境以及相应 IDE
    • 熟悉 ​Spring Boot
    • 熟悉 ​Maven

    现有一张 ​User ​表,其表结构如下:

     id  name  age email 
     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

    其对应的数据库 ​Schema ​脚本如下:

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

    其对应的数据库 ​Data ​脚本如下:

    DELETE FROM user;
    
    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');

    初始化工程

    创建一个空的 Spring Boot 工程(工程将以 H2 作为默认数据库进行演示)

    提示:可以使用 Spring Initializer快速初始化一个 Spring Boot 工程

    添加依赖

    引入 Spring Boot Starter 父工程:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.4</version>
        <relativePath/>
    </parent>
    

    引入 ​spring-boot-starter​、​spring-boot-starter-test​、​mybatis-plus-boot-starter​、​h2 ​依赖:

    <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>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    配置

    在 ​application.yml​ 配置文件中添加 ​H2 ​数据库的相关配置:

    # DataSource Config
    spring:
      datasource:
        driver-class-name: org.h2.Driver
        schema: classpath:db/schema-h2.sql
        data: classpath:db/data-h2.sql
        url: jdbc:h2:mem:test
        username: root
        password: test
    

    在 Spring Boot 启动类中添加 ​@MapperScan​ 注解,扫描 Mapper 文件夹:

    @SpringBootApplication
    @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }

    编码

    编写实体类 ​User.java​(此处使用了Lombok简化代码)

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

    编写 Mapper 包下的 ​UserMapper​接口

    public interface UserMapper extends BaseMapper<User> {
    
    }

    开始使用

    添加测试类,进行功能测试:

    @SpringBootTest
    public class SampleTest {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        public void testSelect() {
            System.out.println(("----- selectAll method test ------"));
            List<User> userList = userMapper.selectList(null);
            Assert.assertEquals(5, userList.size());
            userList.forEach(System.out::println);
        }
    
    }
    

    提示:​UserMapper中的 ​selectList()​ 方法的参数为 ​MP ​内置的条件封装器 ​Wrapper​,所以不填写就是无任何条件

    控制台输出:

    User(id=1, name=Jone, age=18, email=test1@baomidou.com)
    User(id=2, name=Jack, age=20, email=test2@baomidou.com)
    User(id=3, name=Tom, age=28, email=test3@baomidou.com)
    User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
    User(id=5, name=Billie, age=24, email=test5@baomidou.com)

    小结

    通过以上几个简单的步骤,我们就实现了 ​User ​表的 ​CRUD​ 功能,甚至连 ​XML ​文件都不用编写!
    从以上步骤中,我们可以看到集成​MyBatis-Plus​非常的简单,只需要引入 ​starter ​工程,并配置 ​mapper ​扫描路径即可。
    但​ MyBatis-Plus​ 的强大远不止这些功能,想要详细了解 ​MyBatis-Plus​ 的强大功能?那就继续往下看吧!

    MyBatis-Plus 快速入门-安装

    2022-03-23 16:02 更新
    全新的 ​MyBatis-Plus​ 3.0 版本基于 JDK8,提供了 ​lambda ​形式的调用,所以安装集成 MP3.0 要求如下:
    • JDK 8+
    • Maven or Gradle

    Release

    Spring Boot

    Maven:

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

    Gradle:

    compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.5.1'
    

    Spring

    Maven:

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus</artifactId>
        <version>3.5.1</version>
    </dependency>
    

    Gradle:

    compile group: 'com.baomidou', name: 'mybatis-plus', version: '3.5.1'

    注意:引入 ​MyBatis-Plus​ 之后请不要再次引入 ​MyBatis ​以及 ​MyBatis-Spring​,以避免因版本差异导致的问题。

    Snapshot

    快照 SNAPSHOT 版本需要添加仓库,且版本号为快照版本 点击查看最新快照版本号

    Maven:

    <repository>
        <id>snapshots</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    </repository>
    

    Gradle:

    repositories {
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
    }

    MyBatis-Plus 快速入门-配置

    2022-03-23 16:02 更新

    MyBatis-Plus​ 的配置异常的简单,我们仅需要一些简单的配置即可使用 ​MyBatis-Plus​ 的强大功能!

    Spring Boot 工程

    • 配置 ​MapperScan ​注解
    @SpringBootApplication
    @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }
    

    Spring 工程

    • 配置 ​MapperScan
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.baomidou.mybatisplus.samples.quickstart.mapper"/>
    </bean>
    
    • 调整 ​SqlSessionFactory ​为 ​MyBatis-Plus​ 的 ​SqlSessionFactory
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    

    通常来说,一般的简单工程,通过以上配置即可正常使用 ​MyBatis-Plus

    MyBatis-Plus 快速入门-注解

    2022-03-23 16:02 更新

    本文将介绍 MybatisPlus 注解包相关类详解

    @TableName

    • 描述:表名注解,标识实体类对应的表
    • 使用位置:实体类
    @TableName("sys_user")
    public class User {
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
     属性 类型  必须指定  默认值  描述 
     ​values  String  否  ""  表名
     ​schema  String  否  ""  schema
     ​keepGlobalPrefix  boolean  否  false  是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时)
     ​resultMap  String  否  ""  xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定)
     ​autoResultMap  boolean  否  false  是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入)
     ​excludeProperty  String[]  否  {}  需要排除的属性名

    关于 ​autoResultMap​ 的说明:

    MP 会自动构建一个 ​​resultMap​并注入到 ​MyBatis ​里(一般用不上),请注意以下内容:

    因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 Entity 字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。

    而对于 ​typeHandler属性,MyBatis 只支持写在 2 个地方:

    1. 定义在 ​resultMap ​里,作用于查询结果的封装
    2. 定义在 ​insert ​和 ​update ​语句的 ​#{property}​ 中的 ​property ​后面(例:​#{property,typehandler=xxx.xxx.xxx}​),并且只作用于当前 设置值

    除了以上两种直接指定 ​typeHandler ​的形式,MyBatis 有一个全局扫描自定义 ​typeHandler ​包的配置,原理是根据您的 ​property ​类型去找其对应的 ​typeHandler ​并使用。

    @TableId

    • 描述:主键注解
    • 使用位置:实体类主键字段
    @TableName("sys_user")
    public class User {
        @TableId
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
     属性 类型  必须指定  默认值  描述 
     value String  否  ""  主键字段名 
     type Enum  否   IdType.NONE  指定主键类型

    IdType

     值 描述 
    AUTO  数据库 ID 自增
    NONE  无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
    INPUT  insert 前自行 set 主键值
    ASSIGN_ID  分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口​IdentifierGenerator​的方法​nextId​(默认实现类为​DefaultIdentifierGenerator​雪花算法)
    ASSIGN_UUID  分配 UUID,主键类型为 String(since 3.3.0),使用接口​IdentifierGenerator​的方法​nextUUID​(默认 default 方法)
    ID_WORKER  分布式全局唯一 ID 长整型类型(please use ​ASSIGN_ID​)
    UUID  32 位 UUID 字符串(please use ​ASSIGN_UUID​)
    ID_WORKER_STR  分布式全局唯一 ID 字符串类型(please use ​ASSIGN_ID​)

    @TableField

    描述:字段注解(非主键)

    @TableName("sys_user")
    public class User {
        @TableId
        private Long id;
        @TableField("nickname")
        private String name;
        private Integer age;
        private String email;
    }
    
     属性 类型  必须指定  默认值  描述 
    value  String  否  "" 数据库字段名
    exist  boolean  否  true 是否为数据库表字段
    ondition  String  否  "" 字段​where​实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的​%s=#{%s}
    update  String  否  "" 字段 ​update set​ 部分注入,例如:当在version字段上注解​update="%s+1"​ 表示更新时会 ​set version=version+1​ (该属性优先级高于 ​el​ 属性)
    insertStrategy  Enum  否  FieldStrategy.DEFAULT 举例:NOT_NULL
    insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
    updataStrategy  Enum  否  FieldStrategy.DEFAULT 举例:IGNORED
    update table_a set column=#{columnProperty}
    whereStrategy  Enum  否  FieldStrategy.DEFAULT 举例:NOT_EMPTY
    where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
    fill  Enum  否  FieldFill.DEFAULT 字段自动填充策略
    select  boolean  否  true 是否进行select查询
    keepGlobalFormat  boolean  否  false 是否保持使用全局的format进行处理
    jabcType  jabcType  否  JdbcType.UNDEFINED JDBC类型(该默认值不代表会按照该值生效)
    typeHandler  Class<?extends TypeHandler>  否  UnknownTypeHandler.class 类型处理器(该默认值不代表会按照该值生效)
    numericScale  String  否  "" 指定小数点后保留的位数

    关于​jdbcType​和​typeHandler​以及​numericScale​的说明:
    numericScale​只生效于 update 的 sql. ​jdbcType​和​typeHandler​如果不配合​@TableName#autoResultMap = true​一起使用,也只生效于 update 的 sql. 对于​typeHandler​如果你的字段类型和 set 进去的类型为​equals​关系,则只需要让你的​typeHandler​让 Mybatis 加载到即可,不需要使用注解

    FieldStrategy

     值 描述 
    IGNORED 忽略判断
    NOT_NULL 非 NULL 判断
    NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非 NULL 判断)
    DEFAULT 追随全局配置

    FieldFill

     值  描述
    DEFAULT 默认不处理
    INSERT 插入时填充字段
    UPDATE 更新时填充字段
    INSERT_UPDATE 插入和更新时填充字段

    @Version

    • 描述:乐观锁注解、标记 ​@Verison​ 在字段上

    @EnumValue

    • 描述:普通枚举类注解(注解在枚举字段上)

    @TableLogic

    • 描述:表字段逻辑处理注解(逻辑删除)
     属性 类型  必须指定  默认值  描述 
    value  String  否  ""  逻辑未删除值
    delval  String  否  ""  逻辑删除值

    @KeySequence

    • 描述:序列主键策略 ​oracle
    • 属性:​value​、​resultMap
     属性 类型  必须指定  默认值  描述 
    value String  否  ""  序列名 
    clazz Class  否  Long.class   id 的类型, 可以指定 String.class,这样返回的 Sequence 值是字符串"1"

    @OrderBy

    • 描述:内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
     属性 类型  必须指定  默认值  描述 
     isDesc  boolean  否  true  是否倒序查询
     sort short   否  Short.MAX_VALUE  数字越小越靠前

    MyBatis-Plus 核心功能-代码生成器(新)

    2022-03-23 16:06 更新

    注意:适用版本:mybatis-plus-generator 3.5.1 及其以上版本,对历史版本不兼容!

    快速入门

    安装

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.2</version>
    </dependency>
    

    当前包未传递依赖 MP 包,需要自己引入!

    使用

    快速生成

    FastAutoGenerator.create("url", "username", "password")
        .globalConfig(builder -> {
            builder.author("baomidou") // 设置作者
                .enableSwagger() // 开启 swagger 模式
                .fileOverride() // 覆盖已生成文件
                .outputDir("D://"); // 指定输出目录
        })
        .packageConfig(builder -> {
            builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名
                .moduleName("system") // 设置父包模块名
                .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://")); // 设置mapperXml生成路径
        })
        .strategyConfig(builder -> {
            builder.addInclude("t_simple") // 设置需要生成的表名
                .addTablePrefix("t_", "c_"); // 设置过滤表前缀
        })
        .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
        .execute();
    

    交互式生成

    FastAutoGenerator.create(DATA_SOURCE_CONFIG)
        // 全局配置
        .globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称?")).fileOverride())
        // 包配置
        .packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名?")))
        // 策略配置
        .strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
                            .controllerBuilder().enableRestStyle().enableHyphenStyle()
                            .entityBuilder().enableLombok().addTableFills(
                                    new Column("create_time", FieldFill.INSERT)
                            ).build())
        /*
            模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker
           .templateEngine(new BeetlTemplateEngine())
           .templateEngine(new FreemarkerTemplateEngine())
         */
        .execute();
    
    
    // 处理 all 情况
    protected static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }
    

    MyBatis-Plus CRUD接口-Service CRUD 接口

    2022-04-02 11:57 更新
    • 通用 Service CRUD 封装​IService接口,进一步封装 CRUD 采用get​查询单行,​remove​删除,​list​查询集合,​page​分页,前缀命名方式区分Mapper层避免混淆,
    • 泛型 ​​为任意实体对象
    • 建议如果存在自定义通用 Service 方法的可能,请创建自己的IBaseService继承Mybatis-Plus提供的基类
    • 对象Wrapper​为条件构造器

    Save

    // 插入一条记录(选择字段,策略插入)
    boolean save(T entity);
    // 插入(批量)
    boolean saveBatch(Collection<T> entityList);
    // 插入(批量)
    boolean saveBatch(Collection<T> entityList, int batchSize);

    参数说明

     参数名 类型  描述 
     ​entity  T  实体对象
     ​entityList  Collection<T>  实体对象集合
     ​batchSize  int  插入批次数量

    SaveOrUpdate

    // TableId 注解存在更新记录,否插入一条记录
    boolean saveOrUpdate(T entity);
    // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
    boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
    // 批量修改插入
    boolean saveOrUpdateBatch(Collection<T> entityList);
    // 批量修改插入
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    参数说明

     参数名  类型  描述
     ​entity  T  实体对象
     ​updateWrapper  Wrapper<T>  实体对象封装操作类 UpdateWrapper
     ​entityList  Collection<T>  实体对象集合
     ​batchSize  int  插入批次数量

    Remove

    // 根据 entity 条件,删除记录
    boolean remove(Wrapper<T> queryWrapper);
    // 根据 ID 删除
    boolean removeById(Serializable id);
    // 根据 columnMap 条件,删除记录
    boolean removeByMap(Map<String, Object> columnMap);
    // 删除(根据ID 批量删除)
    boolean removeByIds(Collection<? extends Serializable> idList);

    参数说明

     参数名  类型  描述 
     ​queryWrapper  Wrapper<T>  实体包装类 QueryWrapper
     ​id  Serializable  主键 ID
     ​columnMap  Map<String, Object>  表字段 map 对象
     ​idList  Collection<? extends Serializable>  主键 ID 列表

    Update

    // 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
    boolean update(Wrapper<T> updateWrapper);
    // 根据 whereWrapper 条件,更新记录
    boolean update(T updateEntity, Wrapper<T> whereWrapper);
    // 根据 ID 选择修改
    boolean updateById(T entity);
    // 根据ID 批量更新
    boolean updateBatchById(Collection<T> entityList);
    // 根据ID 批量更新
    boolean updateBatchById(Collection<T> entityList, int batchSize);

    参数说明

     参数名  类型   描述 
     ​updateWrapper  Wrapper<T>  实体对象封装操作类 UpdateWrapper
     ​entity  T  实体对象
     ​entityList  Collection<T>  实体对象集合
     ​batchSize  int  更新批次数量

    Get

    // 根据 ID 查询
    T getById(Serializable id);
    // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
    T getOne(Wrapper<T> queryWrapper);
    // 根据 Wrapper,查询一条记录
    T getOne(Wrapper<T> queryWrapper, boolean throwEx);
    // 根据 Wrapper,查询一条记录
    Map<String, Object> getMap(Wrapper<T> queryWrapper);
    // 根据 Wrapper,查询一条记录
    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    参数说明

     参数名  类型  描述
     ​id  Serializable  主键 ID
     ​queryWrapper  Wrapper<T>  实体对象封装操作类 QueryWrapper
     ​throwEx  boolean  有多个 result 是否抛出异常
     ​entity  T  实体对象
     ​mapper  Function<? super Object, V>  转换函数

    List

    // 查询所有
    List<T> list();
    // 查询列表
    List<T> list(Wrapper<T> queryWrapper);
    // 查询(根据ID 批量查询)
    Collection<T> listByIds(Collection<? extends Serializable> idList);
    // 查询(根据 columnMap 条件)
    Collection<T> listByMap(Map<String, Object> columnMap);
    // 查询所有列表
    List<Map<String, Object>> listMaps();
    // 查询列表
    List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
    // 查询全部记录
    List<Object> listObjs();
    // 查询全部记录
    <V> List<V> listObjs(Function<? super Object, V> mapper);
    // 根据 Wrapper 条件,查询全部记录
    List<Object> listObjs(Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询全部记录
    <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    参数说明

     参数名  类型  描述
     ​queryWrapper  Wrapper<T>  实体对象封装操作类 QueryWrapper
     ​idList  Collection<? extends Serializable>  主键 ID 列表
     ​columnMap  Map<String, Object>  表字段 map 对象
     ​mapper  Function<? super Object, V>  转换函数

    Page

    // 无条件分页查询
    IPage<T> page(IPage<T> page);
    // 条件分页查询
    IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
    // 无条件分页查询
    IPage<Map<String, Object>> pageMaps(IPage<T> page);
    // 条件分页查询
    IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
    

    参数说明

     参数名  类型  描述 
     ​page  IPage<T>  翻页对象
     ​queryWrapper  Wrapper<T>  实体对象封装操作类 QueryWrapper

    Count

    // 查询总记录数
    int count();
    // 根据 Wrapper 条件,查询总记录数
    int count(Wrapper<T> queryWrapper);

    参数说明

     参数名  类型  描述
     queryWrapper  Wrapper<T>  实体对象封装操作类 QueryWrapper

    Chain

    query

    // 链式查询 普通
    QueryChainWrapper<T> query();
    // 链式查询 lambda 式。注意:不支持 Kotlin
    LambdaQueryChainWrapper<T> lambdaQuery();
    
    // 示例:
    query().eq("column", value).one();
    lambdaQuery().eq(Entity::getId, value).list();
    

    update

    // 链式更改 普通
    UpdateChainWrapper<T> update();
    // 链式更改 lambda 式。注意:不支持 Kotlin
    LambdaUpdateChainWrapper<T> lambdaUpdate();
    
    // 示例:
    update().eq("column", value).remove();
    lambdaUpdate().eq(Entity::getId, value).update(entity);
    
     

    MyBatis-Plus CRUD接口-Mapper CRUD 接口

    2022-03-23 17:08 更新
    • 通用 CRUD 封装​BaseMapper​接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
    • 泛型 ​​为任意实体对象
    • 参数 ​Serializable ​为任意类型主键, Mybatis-Plus 不推荐使用复合主键约定,每一张表都有自己的唯一 id 主键
    • 对象 ​Wrapper ​为 条件构造器

    Insert

    // 插入一条记录
    int insert(T entity);

    参数说明

     参数名  类型  描述
     entity  T  实体对象

    Delete

    // 根据 entity 条件,删除记录
    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
    // 删除(根据ID 批量删除)
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    // 根据 ID 删除
    int deleteById(Serializable id);
    // 根据 columnMap 条件,删除记录
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    参数说明

     参数名  类型  描述
     ​wrapper  Wrapper<T>  实体对象封装操作类(可以为 null)
     ​idList  Collection<? extends Serializable>  主键 ID 列表(不能为 null 以及 empty)
     ​id  Serializable  主键 ID
     ​columnMap  Map<String, Object>  表字段 map 对象

    Update

    // 根据 whereWrapper 条件,更新记录
    int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
    // 根据 ID 修改
    int updateById(@Param(Constants.ENTITY) T entity);

    参数说明

     参数名  类型  描述
     ​entity  T  实体对象 (set 条件值,可为 null)
     ​updateWrapper  Wrapper<T>  实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)

    Select

    // 根据 ID 查询
    T selectById(Serializable id);
    // 根据 entity 条件,查询一条记录
    T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    // 查询(根据ID 批量查询)
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    // 根据 entity 条件,查询全部记录
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    // 查询(根据 columnMap 条件)
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    // 根据 Wrapper 条件,查询全部记录
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    // 根据 entity 条件,查询全部记录(并翻页)
    IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询全部记录(并翻页)
    IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询总记录数
    Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    参数说明

     参数名  类型  描述
     ​id  Serializable  主键 ID
     ​queryWrapper  Wrapper<T>  实体对象封装操作类(可以为 null)
     ​idList  Collection<? extends Serializable>  主键 ID 列表(不能为 null 以及 empty)
     ​columnMap  Map<String, Object>  表字段 map 对象
     ​page  IPage<T>  分页查询条件(可以为 RowBounds.DEFAULT)

    MyBatis-Plus CRUD接口-SimpleQuery 工具类

    2022-03-24 14:52 更新
    • 对​selectList​查询后的结果用​Stream​流进行了一些封装,使其可以返回一些指定结果,简洁了api的调用
    • 需要项目中已注入对应实体的​BaseMapper
    • 对于下方参数​peeks​,其类型为​Consumer...​,可一直往后叠加操作例如:​List<Long> ids = SimpleQuery.list(Wrappers.lambdaQuery(), Entity::getId, System.out::println, user -> userNames.add(user.getName()));

    keyMap

    // 查询表内记录,封装返回为Map<属性,实体>
    Map<A, E> keyMap(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks);
    // 查询表内记录,封装返回为Map<属性,实体>,考虑了并行流的情况
    Map<A, E> keyMap(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks);

    参数说明

     参数名  类型  描述
     ​entity  E  实体对象
     ​attribute  A  实体属性类型,也是map中key的类型
     ​wrapper  LambdaQueryWrapper<E>  支持lambda的条件构造器
     ​sFunction  SFunction<E, A>  实体中属性的getter,用于封装后map中作为key的条件
     ​isParallel  boolean  为true时底层使用并行流执行
     ​peeks  Consumer<E>...  可叠加的后续操作

    map

    // 查询表内记录,封装返回为Map<属性,属性>
    Map<A, P> map(LambdaQueryWrapper<E> wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, Consumer<E>... peeks);
    // 查询表内记录,封装返回为Map<属性,属性>,考虑了并行流的情况
    Map<A, P> map(LambdaQueryWrapper<E> wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, boolean isParallel, Consumer<E>... peeks);

    参数说明

     参数名  类型  描述
     ​entity  E  实体对象
     ​attribute  A  实体属性类型,也是map中key的类型 
     ​attribute  P  实体属性类型,也是map中value的类型
     ​wrapper  LambdaQueryWrapper<E>  支持lambda的条件构造器
     ​keyFunc  SFunction<E, A>  封装后map中作为key的条件
     ​valueFunc  SFunction<E, P>  封装后map中作为value的条件
     ​isParallel  boolean  为true时底层使用并行流执行
     ​peeks  Consumer<E>...  可叠加的后续操作

    group

    // 查询表内记录,封装返回为Map<属性,List<实体>>
    Map<K, List<T>> group(LambdaQueryWrapper<T> wrapper, SFunction<T, A> sFunction, Consumer<T>... peeks);
    // 查询表内记录,封装返回为Map<属性,List<实体>>,考虑了并行流的情况
    Map<K, List<T>> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, boolean isParallel, Consumer<T>... peeks);
    // 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>
    M group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, Consumer<T>... peeks);
    // 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>,考虑了并行流的情况
    M group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, boolean isParallel, Consumer<T>... peeks);

    参数说明

     参数名  类型  描述
     ​entity  T  实体对象
     ​attribute  K  实体属性类型,也是map中key的类型
     ​-  D  下游收集器返回类型,也是map中value的类型
     ​-  A  下游操作中间类型
     ​-  M  最终结束返回的Map<K, D>
     ​wrapper  LambdaQueryWrapper<E>  支持lambda的条件构造器
     ​sFunction  SFunction<E, A>  分组依据,封装后map中作为key的条件
     ​downstream  Collector<T, A, D>  下游收集器
     ​isParallel  boolean  为true时底层使用并行流执行
     ​peeks  Consumer<T>...  可叠加的后续操作

    list

    // 查询表内记录,封装返回为List<属性>
    List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks);
    // 查询表内记录,封装返回为List<属性>,考虑了并行流的情况
    List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks);

    参数说明

     参数名  类型  描述
     ​entity  E  实体对象
     ​attribute  A  实体属性类型,也是list中元素的类型
     ​wrapper  LambdaQueryWrapper<E>  支持lambda的条件构造器
     ​sFunction  SFunction<E, A>  封装后list中的元素
     ​isParallel  boolean  为true时底层使用并行流执行
     ​peeks  Consumer<E>...  可叠加的后续操作

    MyBatis-Plus 条件构造器-AbstractWrapper

    2022-03-24 16:27 更新

    说明

    QueryWrapper​(LambdaQueryWrapper) 和 ​UpdateWrapper​(LambdaUpdateWrapper) 的父类

    用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件

    注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

     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  

    MyBatis-Plus 条件构造器-QueryWrapper

    2022-03-24 16:03 更新

    说明:

    继承自 ​AbstractWrapper ​,自身的内部属性 ​entity​也用于生成 ​where ​条件

    及 ​LambdaQueryWrapper​, 可以通过 ​new QueryWrapper().lambda()​ 方法获取

    select

    select(String... sqlSelect)
    select(Predicate<TableFieldInfo> predicate)
    select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
    • 设置查询字段

    说明:

    以上方法分为两类.

    第二类方法为:过滤查询字段(主键除外),入参不包含 ​class ​的调用前需要​wrapper​内的​entity​属性有值! 这两类方法重复调用以最后一次为准

    • 例: ​select("id", "name", "age")
    • 例: ​select(i -> i.getProperty().startsWith("test"))

    MyBatis-Plus 条件构造器-UpdateWrapper

    2022-05-11 17:19 更新

    说明:

    继承自 ​AbstractWrapper,自身的内部属性 ​entity也用于生成 ​where条件

    及 ​LambdaUpdateWrapper​, 可以通过 ​new UpdateWrapper().lambda()​ 方法获取!

    set

    set(String column, Object val)
    set(boolean condition, String column, Object val)
    • SQL SET 字段
    • 例: ​set("name", "老李头")
    • 例: ​set("name", "")​--->数据库字段值变为空字符串
    • 例: ​set("name", null)​--->数据库字段值变为​null

    setSql

    setSql(String sql) 
    • 设置 SET 部分 SQL
    • 例: ​setSql("name = '老李头'")

    lambda 

    • 获取 ​LambdaWrapper
    • 在​QueryWrapper​中是获取​LambdaQueryWrapper
    • 在​UpdateWrapper​中是获取​LambdaUpdateWrapper

    MyBatis-Plus 条件构造器-使用 Wrapper 自定义SQL

    2022-03-24 16:27 更新

    注意事项

    需要​mybatis-plus​版本 >= ​3.0.7

    param ​参数名要么叫​ew​,要么加上注解​@Param(Constants.WRAPPER)​ 

    使用​${ew.customSqlSegment}​ 不支持 ​Wrapper内的entity生成where语句

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

    由于​kotlin​相比于​java​多了数据对象(​data class​),在未说明情况下可能会混用。建议按照以下形式定义持久化对象

    @TableName("sys_user")
    class User {
    		@TableId(type = IdType.AUTO)
        var id: Int? = null
    
        @TableField("username")
        var name: String? = null
    
        var roleId: Int? = null
    }
    

    注意:这里的​TableId​及​TableField​并非必要,只是为了展示​Mybatis-Plus​中的​annotation​使用

    这里所有成员都需要定义为可空类型(​?​),并赋予​null​的初始值,方便我们在以下场景中使用(类似java中的​updateSelective​)

    val wrapper = KtUpdateWrapper(User::class.java).eq(User::id, 2)
    val newRecord = User()
    newRecord.name = "newName"
    userMapper!!.update(newRecord, wrapper)
    

    不建议使用​data class​及全参数构造方法,这样我们会写很多不必要的​null​来构造一个空对象

    用注解

    @Select("select * from mysql_data ${ew.customSqlSegment}")
    List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
    

    用XML

    List<MysqlData> getAll(Wrapper ew);
    
    <select id="getAll" resultType="MysqlData">
    	SELECT * FROM mysql_data ${ew.customSqlSegment}
    </select>
    

    kotlin使用wrapper

    kotlin 可以使用 ​QueryWrapper和 ​UpdateWrapper但无法使用 ​LambdaQueryWrapper和 ​LambdaUpdateWrapper

    如果想使用 lambda 方式的 wrapper 请使用 ​KtQueryWrapper和 ​KtUpdateWrapper

    val queryWrapper = KtQueryWrapper(User()).eq(User::name, "sss").eq(User::roleId, "sss2")
    userMapper!!.selectList(queryWrapper)
    
    val updateConditionWrapper = KtUpdateWrapper(User()).eq(User::name, "sss").eq(User::roleId, "sss2")
    val updateRecord = User()
    updateRecord.name = "newName"
    userMapper!!.update(updateRecord, updateConditionWrapper)
    
    val updateRecord = User()
    updateRecord.id = 2
    updateRecord.name = "haha"
    userMapper.updateById(updateRecord)
    

    链式调用 lambda 式

    // 区分:
    // 链式调用 普通
    UpdateChainWrapper<T> update();
    // 链式调用 lambda 式。注意:不支持 Kotlin 
    LambdaUpdateChainWrapper<T> lambdaUpdate();
    
    // 等价示例:
    query().eq("id", value).one();
    lambdaQuery().eq(Entity::getId, value).one();
    
    // 等价示例:
    update().eq("id", value).remove();
    lambdaUpdate().eq(Entity::getId, value).remove();

    MyBatis-Plus 核心功能-主键策略

    2022-03-24 16:31 更新

    提示

    主键生成策略必须使用 ​INPUT

    支持父类定义 ​@KeySequence​ 子类继承使用

    支持主键类型指定(3.3.0 开始自动识别主键类型)

    内置支持:

    • DB2KeyGenerator
    • H2KeyGenerator
    • KingbaseKeyGenerator
    • OracleKeyGenerator
    • PostgreKeyGenerator

    如果内置支持不满足你的需求,可实现 ​IKeyGenerator​接口来进行扩展.

    举个例子:

    @KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)
    public class YourEntity {
    
        @TableId(value = "ID_STR", type = IdType.INPUT)
        private String idStr;
    
    }
    

    Spring-Boot

    方式一:使用配置类

    @Bean
    public IKeyGenerator keyGenerator() {
        return new H2KeyGenerator();
    }
    

    方式二:通过 MybatisPlusPropertiesCustomizer 自定义

    @Bean
    public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
        return plusProperties -> plusProperties.getGlobalConfig().getDbConfig().setKeyGenerator(new H2KeyGenerator());
    }
    

    Spring

    方式一: XML 配置

    <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
       <property name="dbConfig" ref="dbConfig"/>
    </bean>
    
    <bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
       <property name="keyGenerator" ref="keyGenerator"/>
    </bean>
    
    <bean id="keyGenerator" class="com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator"/>
    

    方式二:注解配置

    @Bean
    public GlobalConfig globalConfig() {
    	GlobalConfig conf = new GlobalConfig();
    	conf.setDbConfig(new GlobalConfig.DbConfig().setKeyGenerator(new H2KeyGenerator()));
    	return conf;
    }

    MyBatis-Plus 核心功能-自定义ID生成器

    2022-03-24 17:15 更新

    提示

    自 3.3.0 开始,默认使用雪花算法+​UUID​(不含中划线)

    自定义示例工程:

     方法  主键生成策略  主键类型  说明
    nextId ASSIGN_ID,
    ID_WORKER
    ID_WORKER_STR
    Long,
    Integer,
    String
    支持自动转换为 String 类型,但数值类型不支持自动转换,需精准匹配,例如返回 Long,实体主键就不支持定义为 Integer
    nextUUID ASSIGN_UUID,UUID String 默认不含中划线的 UUID 生成

    Spring-Boot

    方式一:声明为 Bean 供 Spring 扫描注入

    @Component
    public class CustomIdGenerator implements IdentifierGenerator {
        @Override
        public Long nextId(Object entity) {
          	//可以将当前传入的class全类名来作为bizKey,或者提取参数来生成bizKey进行分布式Id调用生成.
          	String bizKey = entity.getClass().getName();
            //根据bizKey调用分布式ID生成
            long id = ....;
          	//返回生成的id值即可.
            return id;
        }
    }
    

    方式二:使用配置类

    @Bean
    public IdentifierGenerator idGenerator() {
        return new CustomIdGenerator();
    }
    

    方式三:通过 MybatisPlusPropertiesCustomizer 自定义

    @Bean
    public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
        return plusProperties -> plusProperties.getGlobalConfig().setIdentifierGenerator(new CustomIdGenerator());
    }
    

    Spring

    方式一: XML 配置

    <bean name="customIdGenerator" class="com.baomidou.samples.incrementer.CustomIdGenerator"/>
    
    <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
    		<property name="identifierGenerator" ref="customIdGenerator"/>
    </bean>
    

    方式二:注解配置

    @Bean
    public GlobalConfig globalConfig() {
    	GlobalConfig conf = new GlobalConfig();
    	conf.setIdentifierGenerator(new CustomIdGenerator());
    	return conf;
    }

    MyBatis-Plus 扩展-逻辑删除

    2022-03-25 13:48 更新

    说明:

    只对自动注入的 sql 起效:

    • 插入: 不作限制
    • 查找: 追加 ​where条件过滤掉已删除数据,且使用 ​wrapper.entity​ 生成的 where 条件会忽略该字段
    • 更新: 追加 ​where条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
    • 删除: 转变为 更新

    例如:

    • 删除: ​update user set deleted=1 where id = 1 and deleted=0
    • 查找: ​select id,name,deleted from user where deleted=0

    字段类型支持说明:

    • 支持所有数据类型(推荐使用 ​Integer​,​Boolean​,​LocalDateTime​)
    • 如果数据库字段使用​datetime​,逻辑未删除值和已删除值支持配置为字符串​null​,另一个值支持配置为函数来获取值如​now()

    附录:

    • 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
    • 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。

    使用方法

    步骤 1

    • 配置​com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig

    例如: ​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)
    

    步骤 2

    • 实体类字段上加上​@TableLogic​注解
    @TableLogic
    private Integer deleted;

    常见问题

    1. 如何 insert ?

    • 字段在数据库定义默认值(推荐)
    • insert前自己 ​set
    • 使用 自动填充功能 

    2. 删除接口自动填充功能失效

    • 使用 ​update方法并: ​UpdateWrapper.set(column, value)​(推荐)
    • 使用 ​update方法并: ​UpdateWrapper.setSql("column=value")
    • 使用 Sql 注入器 注入 ​com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill​ 并使用(推荐)

    MyBatis-Plus 扩展-通用枚举

    2022-03-25 13:48 更新

    解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!

    自3.1.0开始,如果你无需使用原生枚举,可配置默认枚举来省略扫描通用枚举配置

    • 升级说明:

    3.1.0 以下版本改变了原生默认行为,升级时请将默认枚举设置为​EnumOrdinalTypeHandler

    • 影响用户:

    实体中使用原生枚举

    • 其他说明:

    配置枚举包扫描的时候能提前注册使用注解枚举的缓存

    1、声明通用枚举属性

    • 方式一: 使用 ​@EnumValue​ 注解枚举属性
    public enum GradeEnum {
    
        PRIMARY(1, "小学"),  SECONDORY(2, "中学"),  HIGH(3, "高中");
    
        GradeEnum(int code, String descp) {
            this.code = code;
            this.descp = descp;
        }
    
        @EnumValue//标记数据库存的值是code
        private final int code;
        //。。。
    }
    
    • 方式二: 枚举属性,实现 ​IEnum接口如下:
    public enum AgeEnum implements IEnum<Integer> {
        ONE(1, "一岁"),
        TWO(2, "二岁"),
        THREE(3, "三岁");
    
        private int value;
        private String desc;
    
        @Override
        public Integer getValue() {
            return this.value;
        }
    }
    
    • 实体属性使用枚举类型
    public class User {
        /**
         * 名字
         * 数据库字段: name varchar(20)
         */
        private String name;
    
        /**
         * 年龄,IEnum接口的枚举处理
         * 数据库字段:age INT(3)
         */
        private AgeEnum age;
    
    
        /**
         * 年级,原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
         * 数据库字段:grade INT(2)
         */
        private GradeEnum grade;
    }
    

    2、配置扫描通用枚举

    • 配置文件 ​resources/application.yml
    mybatis-plus:
        # 支持统配符 * 或者 ; 分割
        typeEnumsPackage: com.baomidou.springboot.entity.enums
      ....
    • 自定义配置类 ​MybatisPlusAutoConfiguration
    @Configuration
    public class MybatisPlusAutoConfiguration {
    
        @Bean
        public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {
            return properties -> {
                GlobalConfig globalConfig = properties.getGlobalConfig();
                globalConfig.setBanner(false);
                MybatisConfiguration configuration = new MybatisConfiguration();
                configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class);
                properties.setConfiguration(configuration);
            };
        }
    }
    

    如何序列化枚举值为数据库存储值?

    Jackson

    • 重写 ​toString ​方法

    springboot:

        @Bean
        public Jackson2ObjectMapperBuilderCustomizer customizer(){
            return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
        }
    

    jackson:

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
    

    以上两种方式任选其一,然后在枚举中复写 ​toString方法即可。

    • 注解处理
    public enum GradeEnum {
    
        PRIMARY(1, "小学"),  SECONDORY(2, "中学"),  HIGH(3, "高中");
    
        GradeEnum(int code, String descp) {
            this.code = code;
            this.descp = descp;
        }
    
        @EnumValue
      	@JsonValue	//标记响应json值
        private final int code;
    }
    

    Fastjson 

    • 重写 ​toString方法

    全局处理方式

        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString);
    

    局部处理方式

        @JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
        private UserStatus status;
    

    以上两种方式任选其一,然后在枚举中复写 ​toString方法即可。

    MyBatis-Plus 扩展-字段类型处理器

    2022-03-25 13:48 更新

    类型处理器,用于 JavaType 与 JdbcType 之间的转换,用于 ​PreparedStatement设置参数值和从 ​ResultSet或 ​CallableStatement中取出一个值,本文讲解 mybaits-plus 内置常用类型处理器如何通过TableField注解快速注入到 mybatis 容器中。

    • JSON 字段类型
    @Data
    @Accessors(chain = true)
    @TableName(autoResultMap = true)
    public class User {
        private Long id;
    
        ...
    
    
        /**
         * 注意!! 必须开启映射注解
         *
         * @TableName(autoResultMap = true)
         *
         * 以下两种类型处理器,二选一 也可以同时存在
         *
         * 注意!!选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包
         */
        @TableField(typeHandler = JacksonTypeHandler.class)
        // @TableField(typeHandler = FastjsonTypeHandler.class)
        private OtherInfo otherInfo;
    
    }
    

    该注解对应了 XML 中写法为

    <result column="other_info" jdbcType="VARCHAR" property="otherInfo" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />

    MyBatis-Plus 扩展-自动填充功能

    2022-03-25 13:47 更新

    原理: 

    • 实现元对象处理器接口:​com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
    • 注解填充字段 ​@TableField(.. fill = FieldFill.INSERT)​ 生成器策略部分也可以配置!
    public class User {
    
        // 注意!这里需要标记为填充字段
        @TableField(.. fill = FieldFill.INSERT)
        private String fillField;
    
        ....
    }
    • 自定义实现类 ​MyMetaObjectHandler
    @Slf4j
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill ....");
            this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
            // 或者
            this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
            // 或者
            this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill ....");
            this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
            // 或者
            this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
            // 或者
            this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
        }
    }
    

    注意事项:

    • 填充原理是直接给​entity的属性设置值!!!
    • 注解则是指定该属性在对应情况下必有值,如果无值则入库会是​null
    • MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为​null则不填充
    • 字段必须声明​TableField​注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
    • 填充处理器​MyMetaObjectHandler​在 Spring Boot 中需要声明​@Component​或​@Bean​注入
    • 要想根据注解​FieldFill.xxx​和字段名以及字段类型来区分必须使用父类的​strictInsertFill​或者​strictUpdateFill​方法
    • 不需要根据任何来区分可以使用父类的​fillStrategy​方法
    • update(T t,Wrapper updateWrapper)​时​t​不能为空,否则自动填充失效
    public enum FieldFill {
        /**
         * 默认不处理
         */
        DEFAULT,
        /**
         * 插入填充字段
         */
        INSERT,
        /**
         * 更新填充字段
         */
        UPDATE,
        /**
         * 插入和更新填充字段
         */
        INSERT_UPDATE
    }

    MyBatis-Plus 扩展-SQL注入器

    2022-03-25 13:47 更新

    注入器配置

    全局配置 ​sqlInjector用于注入 ​ISqlInjector​接口的子类,实现自定义方法注入。

    参考默认注入器 DefaultSqlInjector

    • SQL 自动注入器接口 ​ISqlInjector
    public interface ISqlInjector {
    
        /**
         * <p>
         * 检查SQL是否注入(已经注入过不再注入)
         * </p>
         *
         * @param builderAssistant mapper 信息
         * @param mapperClass      mapper 接口的 class 对象
         */
        void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass);
    }
    

    自定义自己的通用方法可以实现接口 ​ISqlInjector也可以继承抽象类 ​AbstractSqlInjector注入通用方法 SQL 语句 然后继承 ​BaseMapper添加自定义方法,全局配置 ​sqlInjector注入 ​MP​ 会自动将类所有方法注入到 ​mybatis容器中。

    MyBatis-Plus 扩展-执行SQL分析打印

    2022-03-25 13:47 更新

    该功能依赖 ​p6spy组件,完美的输出打印 SQL 及执行时长 3.1.0 以上版本

    示例工程:

    mybatis-plus-sample-crud

    • p6spy依赖引入

    Maven:

    <dependency>
      <groupId>p6spy</groupId>
      <artifactId>p6spy</artifactId>
      <version>最新版本</version>
    </dependency>
    

    Gradle:

    compile group: 'p6spy', name: 'p6spy', version: '最新版本'
    
    • application.yml​ 配置:
    spring:
      datasource:
        driver-class-name: com.p6spy.engine.spy.P6SpyDriver
        url: jdbc:p6spy:h2:mem:test
        ...
    
    • spy.properties​ 配置:
    #3.2.1以上使用
    modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
    #3.2.1以下使用或者不配置
    #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
    # 自定义日志打印
    logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
    #日志输出到控制台
    appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
    # 使用日志系统记录 sql
    #appender=com.p6spy.engine.spy.appender.Slf4JLogger
    # 设置 p6spy driver 代理
    deregisterdrivers=true
    # 取消JDBC URL前缀
    useprefix=true
    # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
    excludecategories=info,debug,result,commit,resultset
    # 日期格式
    dateformat=yyyy-MM-dd HH:mm:ss
    # 实际驱动可多个
    #driverlist=org.h2.Driver
    # 是否开启慢SQL记录
    outagedetection=true
    # 慢SQL记录标准 2 秒
    outagedetectioninterval=2
    

    注意!

    • driver-class-name​ 为 ​p6spy提供的驱动类
    • url前缀为 ​jdbc:p6spy​ 跟着冒号为对应数据库连接地址
    • 打印出 sql 为 ​null,在 ​excludecategories增加 ​commit
    • 批量操作不打印 sql,去除 ​excludecategories中的 ​batch
    • 批量操作打印重复的问题请使用 ​MybatisPlusLogFactory(3.2.1 新增)
    • 该插件有性能损耗,不建议生产环境使用。

    MyBatis-Plus 扩展-数据安全保护

    2022-03-25 13:47 更新

    该功能为了保护数据库配置及数据安全,在一定的程度上控制开发人员流动导致敏感信息泄露。

    • 3.3.2 开始支持
    • 配置安全 

    YML 配置:

    // 加密配置 mpw: 开头紧接加密内容( 非数据库配置专用 YML 中其它配置也是可以使用的 )
    spring:
      datasource:
        url: mpw:qRhvCwF4GOqjessEB3G+a5okP+uXXr96wcucn2Pev6Bf1oEMZ1gVpPPhdDmjQqoM
        password: mpw:Hzy5iliJbwDHhjLs1L0j6w==
        username: mpw:Xb+EgsyuYRXw7U7sBJjBpA==
    

    密钥加密:

    // 生成 16 位随机 AES 密钥
    String randomKey = AES.generateRandomKey();
    
    // 随机密钥加密
    String result = AES.encrypt(data, randomKey);
    

    如何使用:

    // Jar 启动参数( idea 设置 Program arguments , 服务器可以设置为启动环境变量 )
    --mpw.key=d1104d7c3b616f0b
    

    注意!

    • 加密配置必须以 ​mpw:​ 字符串开头
    • 随机密钥请负责人妥善保管,当然越少人知道越好。

    MyBatis-Plus 扩展-多数据源

    2022-03-25 13:47 更新

    简介

    dynamic-datasource-spring-boot-starter​ 是一个基于springboot的快速集成多数据源的启动器。

    其支持 ​Jdk 1.7+​, ​SpringBoot 1.4.x 1.5.x 2.x.x

    文档

    详细文档

    特性

    • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
    • 支持数据库敏感配置信息 加密 ​ENC()​。
    • 支持每个数据库独立初始化表结构​schema​和数据库​database​。
    • 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
    • 支持 自定义注解 ,需继承​DS(3.2.0+)​。
    • 提供并简化对​Druid​,​HikariCp​,​BeeCp​,​Dbcp2​的快速集成。
    • 提供对​Mybatis-Plus​,​Quartz​,​ShardingJdbc​,​P6sy​,​Jndi​等组件的集成方案。
    • 提供 自定义数据源来源 方案(如全从数据库加载)。
    • 提供项目启动后 动态增加移除数据源 方案。
    • 提供Mybatis环境下的 纯读写分离 方案。
    • 提供使用 ​spel​动态参数 解析数据源方案。内置​spel​,​session​,​header​,支持自定义。
    • 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
    • 提供 **基于seata的分布式事务方案。
    • 提供 本地多数据源事务方案。

    约定

    • 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何​CRUD​。
    • 配置文件所有以下划线 ​_分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
    • 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
    • 默认的数据源名称为 ​master,你可以通过 ​spring.datasource.dynamic.primary​ 修改。
    • 方法上的注解优先于类上注解。
    • DS​支持继承抽象类上的​DS​,暂不支持继承接口上的​DS​。

    使用方法

    • 引入​dynamic-datasource-spring-boot-starter
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
      <version>${version}</version>
    </dependency>
    
    • 配置数据源
    spring:
      datasource:
        dynamic:
          primary: master #设置默认的数据源或者数据源组,默认值即为master
          strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
          datasource:
            master:
              url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
            slave_1:
              url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
            slave_2:
              url: ENC(xxxxx) # 内置加密,使用请查看详细文档
              username: ENC(xxxxx)
              password: ENC(xxxxx)
              driver-class-name: com.mysql.jdbc.Driver
           #......省略
           #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
    # 多主多从                      纯粹多库(记得设置primary)                   混合配置
    spring:                               spring:                               spring:
      datasource:                           datasource:                           datasource:
        dynamic:                              dynamic:                              dynamic:
          datasource:                           datasource:                           datasource:
            master_1:                             mysql:                                master:
            master_2:                             oracle:                               slave_1:
            slave_1:                              sqlserver:                            slave_2:
            slave_2:                              postgresql:                           oracle_1:
            slave_3:                              h2:                                   oracle_2:
    • 使用 ​@DS​ 切换数据源

    @DS​ 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。

     注解  结果
     没有@DS  默认数据源
     @DS("dsName")  dsName可以为组名也可以为具体某个库的名称
    @Service
    @DS("slave")
    public class UserServiceImpl implements UserService {
    
      @Autowired
      private JdbcTemplate jdbcTemplate;
    
      public List selectAll() {
        return  jdbcTemplate.queryForList("select * from user");
      }
      
      @Override
      @DS("slave_1")
      public List selectByCondition() {
        return  jdbcTemplate.queryForList("select * from user where age >10");
      }
    }

    MyBatis-Plus 插件-插件主体

    2022-03-25 14:24 更新

    注意

    • 版本要求:3.4.0 版本以上

    MybatisPlusInterceptor

    该插件是核心插件,目前代理了 ​Executor#query​ 和 ​Executor#update​ 和 ​StatementHandler#prepare​ 方法

    属性

    • private List interceptors = new ArrayList<>();

    InnerInterceptor

    我们提供的插件都将基于此接口来实现功能

    目前已有的功能:

    • 自动分页: ​PaginationInnerInterceptor
    • 多租户: ​TenantLineInnerInterceptor
    • 动态表名: ​DynamicTableNameInnerInterceptor
    • 乐观锁: ​OptimisticLockerInnerInterceptor
    • sql 性能规范: ​IllegalSQLInnerInterceptor
    • 防止全表更新与删除: ​BlockAttackInnerInterceptor

    注意:

    使用多个功能需要注意顺序关系,建议使用如下顺序

    • 多租户,动态表名
    • 分页,乐观锁
    • sql 性能规范,防止全表更新与删除

    总结: 对 sql 进行单次改造的优先放入,不对 sql 进行改造的最后放入

    使用方式(以分页插件举例)

    spring

    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <!-- 其他属性 略 -->
        <property name="configuration" ref="configuration"/>
        <property name="plugins">
            <array>
                <ref bean="mybatisPlusInterceptor"/>
            </array>
        </property>
    </bean>
    
    <bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
        <!-- 需配置该值为false,避免1或2级缓存可能出现问题,该属性会在旧插件移除后一同移除 -->
        <property name="useDeprecatedExecutor" value="false"/>
    </bean>
    
    <bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
        <property name="interceptors">
            <list>
                <ref bean="paginationInnerInterceptor"/>
            </list>
        </property>
    </bean>
    
    <bean id="paginationInnerInterceptor" class="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor">
        <!-- 对于单一数据库类型来说,都建议配置该值,避免每次分页都去抓取数据库类型 -->
        <constructor-arg name="dbType" value="H2"/>
    </bean>
    

    spring-boot

    @Configuration
    @MapperScan("scan.your.mapper.package")
    public class MybatisPlusConfig {
    
        /**
         * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
            return interceptor;
        }
    
        @Bean
        public ConfigurationCustomizer configurationCustomizer() {
            return configuration -> configuration.setUseDeprecatedExecutor(false);
        }
    }
    

    mybatis-config.xml

    <plugins>
      <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
        <property name="@page" value="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor"/>
        <property name="page:dbType" value="h2"/>
      </plugin>
    </plugins>
    

    property的配置说明详见 ​MybatisPlusInterceptor#setProperties​ 的源码方法注释

    拦截忽略注解 @InterceptorIgnore

     属性名  类型  默认值  描述
     tenantLine  String  ""  行级租户
     dynamicTableName  String  ""  动态表名
     blockAttack  String  ""  攻击 SQL 阻断解析器,防止全表更新与删除
     illegalSql  String  ""  垃圾 SQL 拦截

    该注解作用于 ​xxMapper.java​ 方法之上 各属性代表对应的插件 各属性不给值则默认为 ​false设置为 ​true忽略拦截 更多说明详见源码注释

    MyBatis-Plus 插件-分页插件

    2022-03-25 14:30 更新

    PaginationInnerInterceptor

    支持的数据库

    • mysql,oracle,db2,h2,hsql,sqlite,postgresql,sqlserver,Phoenix,Gauss ,clickhouse,Sybase,OceanBase,Firebird,cubrid,goldilocks,csiidb
    • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库

    属性介绍

     属性名  类型  默认值  描述
     overflow  boolean  false  溢出总页数后是否进行处理,默认不处理
     maxLimit  Long    单页分页条数限制,默认无限制
     dbType  DbType    数据库类型,根据类型获取应使用的分页方言
     dialect  IDialect    方言实现类

    建议单一数据库类型的均设置 ​dbType

    自定义的 mapper#method 使用分页

    IPage<UserVo> selectPageVo(IPage<?> page, Integer state);
    // or (class MyPage extends Ipage<UserVo>{ private Integer state; })
    MyPage selectPageVo(MyPage page);
    // or
    List<UserVo> selectPageVo(IPage<UserVo> page, Integer state);
    <select id="selectPageVo" resultType="xxx.xxx.xxx.UserVo">
        SELECT id,name FROM user WHERE state=#{state}
    </select>
    
    • 如果返回类型是 ​IPage则入参的 ​IPage不能为​null​,因为 返回的​IPage ==​ 入参的​IPage
    • 如果返回类型是 ​List则入参的 ​IPage可以为 ​null(为 ​null则不分页),但需要你手动 入参的​IPage.setRecords​(返回的 ​List​);
    • 如果 ​xml​需要从 ​page里取值,需要 ​page.属性​ 获取 

    其他:

    • 生成 ​countSql会在 ​left join​ 的表不参与 ​where条件的情况下,把 ​left join​ 优化掉
    • 所以建议任何带有 ​left join​ 的sql,都写标准sql,即给于表一个别名,字段也要 ​别名.字段

    MyBatis-Plus 插件-乐观锁插件

    2022-03-25 14:35 更新

    OptimisticLockerInnerInterceptor

    当要更新一条记录的时候,希望这条记录没有被别人更新

    乐观锁实现方式:

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

    乐观锁配置需要两步

    1、配置插件

    • spring xml 方式:
    <bean class="com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor" id="optimisticLockerInnerInterceptor"/>
    
    <bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
        <property name="interceptors">
            <list>
                <ref bean="optimisticLockerInnerInterceptor"/>
            </list>
        </property>
    </bean>
    
    • spring boot 注解方式:
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
    

    2、在实体类的字段上加上@Version注解

    @Version
    private Integer version;
    

    说明:

    • 支持的数据类型只有:​int,​Integer,​long​,​Long​,​Date​,​Timestamp​,​LocalDateTime
    • 整数类型下 ​newVersion = oldVersion + 1
    • newVersion会回写到 ​entity
    • 仅支持 ​updateById(id)​ 与 ​update(entity, wrapper)​ 方法
    • 在 ​update(entity, wrapper)​ 方法下, ​wrapper不能复用!!!

    示例:

    // Spring Boot 方式
    @Configuration
    @MapperScan("按需修改")
    public class MybatisPlusConfig {
        /**
         * 旧版
         */
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    
        /**
         * 新版
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return mybatisPlusInterceptor;
        }
    }

    MyBatis-Plus 插件-多租户插件

    2022-03-25 14:38 更新

    TenantLineInnerInterceptor

    示例工程:mybatis-plus-sample-tenant

    属性介绍

     属性名  类型  默认值  描述
     ​tenantLineHandler  TenantLineHandler    租户处理器( TenantId 行级 )
    public interface TenantLineHandler {
    
        /**
         * 获取租户 ID 值表达式,只支持单个 ID 值
         * <p>
         *
         * @return 租户 ID 值表达式
         */
        Expression getTenantId();
    
        /**
         * 获取租户字段名
         * <p>
         * 默认字段名叫: tenant_id
         *
         * @return 租户字段名
         */
        default String getTenantIdColumn() {
            return "tenant_id";
        }
    
        /**
         * 根据表名判断是否忽略拼接多租户条件
         * <p>
         * 默认都要进行解析并拼接多租户条件
         *
         * @param tableName 表名
         * @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
         */
        default boolean ignoreTable(String tableName) {
            return false;
        }
    }
    

    说明:

    • 多租户 != 权限过滤,不要乱用,租户之间是完全隔离的!!!
    • 启用多租户后所有执行的​method​的sql都会进行处理.
    • 自写的sql请按规范书写(sql涉及到多个表的每个表都要给别名,特别是 ​inner join​ 的要写标准的 ​inner join​)

    MyBatis-Plus 插件-防全表更新与删除插件

    2022-03-25 14:40 更新

    BlockAttackInnerInterceptor

    针对 ​update和 ​delete语句,作用: 阻止恶意的全表更新删除

    注入​MybatisPlusInterceptor​类,并配置​BlockAttackInnerInterceptor​拦截器

    @Configuration
    public class MybatisPlusConfig {
      @Bean
      public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
      }
    }
    

    测试示例(全表更新)

    @SpringBootTest
    public class QueryWrapperTest {
    
      @Autowired
      private UserService userService;
    
      /**
      + SQL:UPDATE user  SET name=?,email=?;
      */
      @Test
      public void test() {
        User user = new User();
        user.setId(999L);
        user.setName("custom_name");
        user.setEmail("xxx@mail.com");
        // com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
        userService.saveOrUpdate(user, null);
      }
    }
    

    测试示例(部分更新)

    @SpringBootTest
    public class QueryWrapperTest {
    
      @Autowired
      private UserService userService;
    
      /**
      + SQL:UPDATE user  SET name=?, email=? WHERE id = ?;
      */
      @Test
      public void test() {
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getId, 1);
        User user = new User();
        user.setId(10L);
        user.setName("custom_name");
        user.setEmail("xxx@mail.com");
        userService.saveOrUpdate(user, wrapper);
      }}

    MyBatis-Plus 插件-动态表名插件

    2022-03-25 14:43 更新

    DynamicTableNameInnerInterceptor

    简单示例:mybatis-plus-sample-dynamic-tablename

    注意事项:

    • 原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换
    • 例如:真实表名为 ​user设定为 ​mp_dt_user处理器替换为 ​user_2019
  • 相关阅读:
    DLL相关
    设备实时监控
    VC++定时器的运用
    iOS开发多线程篇—GCD的常见用法
    iOS开发多线程篇—线程的状态
    iOS开发多线程篇—GCD介绍
    iOS开发多线程篇—线程间的通信
    iOS开发多线程篇—线程安全
    iOS开发多线程篇—创建线程
    iOS开发多线程篇—多线程简单介绍
  • 原文地址:https://www.cnblogs.com/hanease/p/16392954.html
Copyright © 2020-2023  润新知