MyBatis-Plus平常工做学习

一:Mybatis-Plus概述

  MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的加强工具,在 MyBatis 的基础上只作加强不作改变,为简化开发、提升效率而生。国人的骄傲呀!!html

(1):特性

  • 无侵入:只作加强不作改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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、DB二、H二、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操做智能分析阻断,也可自定义拦截规则,预防误操做

具体代码下载:java

git clone https://gitee.com/antLaddie/mybatis_plus_demo.gitmysql

二:MapperCRUD接口操做

  咱们在Mapper层上使用CRUD时就须要在具体的接口上继承BaseMapper接口,为Mybatis-Plus启动时自动解析实体表关系映射转换为Mybatis内部对象注入容器;其中Searializable为任意类型的主键,Mybatis-Plus不推荐使用复合主键约定每一张表都有本身的惟一ID主键git

Mybatis_Plus基本必备注解说明:
    ①:@TableId:    此注解表明当前实体字段对应数据库表主键
        type:设置主键生成类型
        value:设置实体字段与数据库字段映射不一致
    ②:@TableField  此注解表明当前实体字段对应数据库表普通字段
        value:设置实体字段与数据库字段映射不一致
        fill: 设置填充模式
    ③:@TableName   映射到数据库表名称
        value:具体映射表名

1:Insert插入方法

// 插入一条记录  T entity 传入对应的实体类便可
int insert(T entity);

注意事项:在使用保存或根据ID查询、更新、删除时,主键字段必须添加@TableId算法

/**
 * @Auther: xiaoYang
 * @Date: 2021/4/6 12:38
 * @Description: 学生实体类
 */
//下面四个都是lombok插件注解
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student implements Serializable {

    @TableId    //必须添加此注解表明当前实体的主键
    private Long sid;           //学生ID
    private String sname;       //姓名
    private String ssex;        //性别
    private Integer sage;       //年龄
    private String saddress;    //住址
    private Integer isDel;      //是否被删除 0未删除 1删除
    private Date createTime;    //数据行建立时间
    private Date updateTime;    //数据行更新时间
    private Integer version;    //版本(用于乐观锁)
}
在实体类上必加@TableId

2:Select查询方法

  Mapper层上也实现了特别多的基本查询方法,如id查询、条件查询、分页查询、统计查询...spring

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

## 具体方法 // 根据 ID 查询
T selectById(Serializable id); // 根据 entity 条件,查询一条记录
// 指定的条件查询查询到多条数据则出现异常
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据ID 批量查询)
// 批量查询 底层使用的是 where xx in ( *** )  IN关键字
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 entity 条件,查询所有记录  全表查询则传入null
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据 columnMap 条件)
// 传入map类型,多个键值则为  aaa = xxx AND bbb = xxx
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 Wrapper 条件,查询所有记录
//以键值对的形式返回 [ {xx=xx,...},{xx=xx,...} ]
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);
// ### 示例 
@Test
    public void selectList() {
        //使用条件构造器模糊查询带 %壮%的名称
        QueryWrapper<Student> wrapper = new QueryWrapper<Student>().like("sname", "壮");
        List<Student> students = studentMapper.selectList(wrapper);
        students.forEach(System.out::println);
    }
Select基本示例

3:Update更新方法

  Mapper也提供了更新方法,能够根据ID来更新一条,或者根据条件查询更新多条sql

// 根据 ID 修改    
int updateById(@Param(Constants.ENTITY) T entity);
// 根据 whereEntity 条件,更新记录
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
    // 根据 ID 修改    一次更新一个
    // int updateById(@Param(Constants.ENTITY) T entity);
    @Test
    public void updateById() {
        //设置基本信息
        Student stu = new Student();
        stu.setSname("蚂蚁小哥");
        stu.setSsex("男");
        stu.setSaddress("上海浦东");
        stu.setSid(1379438319679049730L);   //必须设置ID 要否则找不到
        int i = studentMapper.updateById(stu);
        System.out.println("更新记录条数:" + i);
    }

    // 根据 whereEntity 条件,更新记录  能够一次性更新多个
    // int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
    @Test
    public void update() {
        //设置基本信息    更改年龄66
        Student stu = new Student();
        stu.setSage(66);
        int i = studentMapper.update(stu, new QueryWrapper<Student>().eq("ssex", "保密"));
        System.out.println("更新记录条数:" + i);
    }
Update基本示例

4:Delete删除方法

  Mapper其实也提供了删除方法,如根据ID删除、批量删除、条件删除....数据库

// 根据 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);
    // 根据 entity 条件,删除记录
    // int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
    @Test
    public void delete() {
        // 删除名字中带 %壮% 的名字
        int i = studentMapper.delete(new QueryWrapper<Student>().like("sname", "壮"));
        System.out.println("删除个数:" + i);
    }

    // 删除(根据ID 批量删除)
    // int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    @Test
    public void deleteBatchIds() {
        // 传入集合  删除ID为 2 4 6 8
        int i = studentMapper.deleteBatchIds(Arrays.asList(2, 4, 6, 8));
        System.out.println("删除个数:" + i);
    }
Delete基本示例

5:Page分页查询及分页插件

  细心的话,你们会发现,写完了分页查询后却不起效果,其实这是由于分页查询是须要配置Mybatis-Plus指定插件的;但要注意的是,分页查询和咱们上面的普通查询是不同的,咱们得配置mybatis-plus特有的分页插件才可使用,具体配置以下:apache

@Configuration
@MapperScan(basePackages = "cn.xw.mapper")  //映射mapper扫描
public class MybatisPlusConfig {

    //在这个Bean对象里面配置咱们所需的插件,这里咱们只须要配置一个分页插件便可
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //拦截器对象
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //建立分页插件 并指定为mysql数据库方言 也能够经过对象set设置各类属性
        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        //配置的每页最多显示多少条数据
        pageInterceptor.setMaxLimit(10L);
        // 放入添加分页拦截器插件
        interceptor.addInnerInterceptor(pageInterceptor);
        return interceptor;
    }
}
自定义config配置类==>分页
@SpringBootTest
public class PageTests {

    @Autowired
    private StudentMapper studentMapper;

    // 根据 entity 条件,查询所有记录(并翻页)
    // IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    @Test
    public void selectPage() {
        //分页对象
        Page<Student> page = new Page<>(1, 10);
        //分页查询
        Page<Student> pageResult = studentMapper.selectPage(page, null);
        System.out.println("总记录数:" + pageResult.getTotal());
        System.out.println("每页总数" + pageResult.getSize());
        System.out.println("当前页:" + pageResult.getCurrent());
        System.out.println("排序字段信息:" + pageResult.getOrders());
        pageResult.getRecords().forEach(System.out::println);
    }
    
    // 根据 Wrapper 条件,查询所有记录(并翻页)
    // IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    @Test
    public void selectMapsPage() {
        //分页对象
        Page<Map<String, Object>> page = new Page<>(1, 5);
        //分页查询
        Page<Map<String, Object>> pageResult = studentMapper.selectMapsPage(page, null);
        System.out.println("总记录数:" + pageResult.getTotal());
        System.out.println("每页总数" + pageResult.getSize());
        System.out.println("当前页:" + pageResult.getCurrent());
        System.out.println("排序字段信息:" + pageResult.getOrders());
        List<Map<String, Object>> list = pageResult.getRecords();
        for (Map<String, Object> li : list) {
            System.out.println("姓名:" + li.get("sname") + "  地址:" + li.get("saddress"));
        }
    }
}
使用mybatis-plus插件来分页测试

6:Sequence主键增加策略及主键生成策略

  其实mybatis-plus内置了几个主键增加策略,咱们只须要经过@TableId注解里的type来完成对其设置,也内置了主键增加策略缓存

##  ASSIGN_ID(雪花算法)

  若是咱们不设置主键类型值,默认则使用 IdType.ASSIGN_ID 策略(自3.3.0起)该策略会使用雪花算法自动生成主键ID,主键类型为长整型或字符串(分别对应的MySQL的表字段为BIGINT和VARCHAR)

  该策略使用接口IdentifierGenerator的方法nextId(以实现类为DefaultIdentifierGenerator雪花算法)

  雪花算法(雪花)是微博开源的分布式ID生成算法其核心思想就是:使用一个64位的长整型的数字做为全局惟一ID。在分布式系统中的应用十分普遍,且ID引入了时间戳,基本上保持自增的

@Data
public class Student implements Serializable {
    @TableId(type = IdType.ASSIGN_ID)   //使用雪花算法生成主键  不写也是使用雪花算法
    private Long sid;           //学生ID
    private String sname;       //姓名
  ......省略
}

##  ASSIGN_UUID(去除了中横线的UUID)

  若是使用 IdType.ASSIGN_UUID 策略,并从新自动生成排除中划线的UUID做为主键。主键类型为String,对应MySQL的表分段为VARCHAR(32)

  该策略使用接口IdentifierGenerator的方法nextUUID

@Data
public class Student implements Serializable {
    @TableId(type = IdType.ASSIGN_UUID)   //使用了UUID(没有中横线)的方式生成主键 注意的是主键字段是varchar(32)才行
    private Long sid;           //学生ID
    private String sname;       //姓名
  ......省略
}

##  AUTO(数据库自增加 适用于MySQL)

   对于MySQL适用于这种自增加,可是咱们在建立数据库字段主键时别忘了设置auto_increment

@Data
public class Student implements Serializable {
    // 对于像MySQL这样的支持主键自动递增的数据库,咱们可使用IdType.AUTO策略
    @TableId(type = IdType.AUTO)   // 特别注意 数据库要加 auto_increment
    private Long sid;           //学生ID
    private String sname;       //姓名
  ......省略
}

##  INPUT(插入前自行设置主键自增)

  针对有序列的数据库:Oracle,SQLServer等,当须要创建一个自增序列时,须要用到序列

  在Oracle 11g中,设置自增扩,须要先建立序列(SQUENCE)再建立一个触发器(TRIGGER)

  在Oracle 12c中,只须要使用IDENTITY属性就能够了,和MySQL同样简单

     Mybatis -Plus已经定义好了常见的数据库主键序列,咱们首先只须要在@Configuration类中定义好@Bean:

  Mybatis -Plus内置了以下数据库主键序列(若是内置支持不知足你的需求,可实现IKeyGenerator接口来进行扩展):

  DB2KeyGenerator、H2KeyGenerator、KingbaseKeyGenerator、OracleKeyGenerator、PostgreKeyGenerator

// 配置类
@SpringBootConfiguration
@MapperScan(basePackages = "cn.xw.mapper")  //映射包扫描
public class MybatisPlusConfig {
    // 配置主键序列方式
    @Bean
    public IKeyGenerator keyGenerator() {
        //建立Oracle序列
        return new OracleKeyGenerator();
    }
}

  而后实体类配置主键Sequence,指定主键策略为IdType.INPUT便可;支持父类定义@KeySequence子类使用,这样就能够几个表共享一个Sequence

  若是主键是String类型的,也可使用;如何使用序列做为主键,可是实体主键类型是字符串开头,表的主键是varchar2,可是须要从序列中取值

  实体定义@KeySequence注解clazz指定类型String.class

  实体定义主键的类型字符串

  注意:oracle的序列返回的是Long类型,若是主键类型是Integer,可能会引发ClassCastException

@KeySequence(value = "SQL_TEST", clazz = String.class)
public class Student implements Serializable {
    @TableId(type = IdType.INPUT) private Long sid;           //学生ID
    private String sname;       //姓名
    ...后面省略
}

 7:自动填充功能

   咱们试想这样一个场景,咱们每次添加数据时有个字段是记录数据建立时间的createTime;那么咱们修改数据时是要由updateTime来记录咱们何时修改了数据;前提咱们使用自动填充功能实现时数据库建立类型不要以timestamp时间戳,由于这样数据库层面就能够实现此功能

  自动填充功能不光能够实现时间的自动填充,其它的咱们有需求都是能够实现的

   实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

  注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也能够配置

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@TableName(value = "student")   // 数据库表名称
public class Student implements Serializable {
    //雪花ID
    @TableId(type = IdType.ASSIGN_ID)private Long sid;           //学生ID
    private String sname;       //姓名
    private String ssex;        //性别
    private Integer sage;       //年龄
    private String saddress;    //住址
    private Integer isDel;      //是否被删除 0未删除 1删除
    @TableField(value = "create_time", fill = FieldFill.INSERT) private Date createTime;    //数据行建立时间
    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) private Date updateTime;    //数据行更新时间
    private Integer version;    //版本(用于乐观锁)
}
@Slf4j  //日志
@Component  //加入容器
public class MyMetaObjectHandler implements MetaObjectHandler {

    // 添加数据时的填充方案
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        Date currentDate = new Date();
        // 插入建立时间
        if (metaObject.hasSetter("createTime")) {
            this.strictInsertFill(metaObject, "createTime", Date.class, currentDate);
        }
        // 同时设置修改时间为当前插入时间
        if (metaObject.hasSetter("updateTime")) {
            this.strictUpdateFill(metaObject, "updateTime", Date.class, currentDate);
        }
        //this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
    }

    // 更新数据时的填充方案
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
    }
}
配置自定义handler,填充类实现方案
public enum FieldFill {  //取值范围
    //默认不处理
    DEFAULT,
    //插入填充字段
    INSERT,
    //更新填充字段
    UPDATE,
    //插入和更新填充字段
    INSERT_UPDATE
}

8:逻辑删除

  其实逻辑删除不是真正来删除具体的数据,而是要对删除的数据使用某个字段来记录当前数据是否被删除了,咱们一般使用 is_del 来记录 0 表明未删除 1 表明被删除;此时个人表是存在 is_del 字段的,因此咱们就能够认为每次删除就等于更新 is_del 字段值;可是咱们得对mybatis_plus配置后才能够生效

在application.yml里配置mybatis-plus:标红的才是咱们要配置的
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志输出到控制台 global-config: db-config: logic-delete-field: isDel # 全局逻辑删除的实体字段名(since 3.3.0,配置后能够忽略实体上不配置 @TableLogic) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) logic-delete-value: 1 # 逻辑已删除值(默认为 1)
在实体类上添加 @TableLogic 注解:
@TableName(value = "student")
public class Student implements Serializable {
//使用雪花算法生成主键 不写也是使用雪花算法
@TableId(type = IdType.ASSIGN_ID)
@TableField(value = "sid")
private Long sid; //学生ID
  ......省略部分
@TableLogic // 在配置里配置了logic-delete-field属性后可不加此注解 在3.3.0版本以上才能够不加
private Integer isDel; //是否被删除 0未删除 1删除
......省略部分
}

  还有一个问题就是,咱们在添加数据的时候,咱们得设置 is_del 字段数据值为 0 (未删除)吗?其实这可使用自动填充功能完成,可是最好使用在数据库定义默认值 is_del  int  default 0

三:Mybatis-Plus插件

  在学习插件时首先介绍一下 MybatisPlusInterceptor 它是核心插件 目前代理了 Executor#query 和 Executor#update 和StatementHandler#prepare 方法

MybatisPlusInterceptor 属性: private List<InnerInterceptor> interceptors = new ArrayList<>();
InnerInterceptor:它是一个接口,咱们提供的插件都将基于此接口来实现功能
   目前以实现此接口的类: 自动分页: PaginationInnerInterceptor 多租户: TenantLineInnerInterceptor 动态表名: DynamicTableNameInnerInterceptor 乐观锁: OptimisticLockerInnerInterceptor sql性能规范: IllegalSQLInnerInterceptor 防止全表更新与删除: BlockAttackInnerInterceptor

  注意:

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

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

   总结: 对sql进行单次改造的优先放入,不对sql进行改造的最后放入#使用方式(以分页插件举例)

1:分页插件

  在上面的分页查询有过介绍

2:乐观锁插件

  乐观锁采用了更加宽松的加锁机制,他相信其它人不会来和它争抢锁的,因此乐观锁更倾向于开发运用,不对数据进行加锁,能够提升数据库的性能,而悲观锁就有点悲观了,利用数据库的锁机制实现,把要操做的数据或者表进行加锁,其它人不给操做,只有等它操做完了才轮到后面人使用,这每每来讲就加大了数据库的性能开销

在mybatis-plus里的乐观锁实现方式:
    ①:取出记录时,获取当前version字段值 ②:更新时,带上version字段值 ③:执行更新时比对获取的version和数据库已有的version是否一致 set version = newVersion where version = oldVersion
    ④:若是提交的version和数据库里的version不对应则更新失败

首先配置乐观锁插件:

@SpringBootConfiguration    //声明为配置类
@MapperScan(basePackages = "cn.xw.mapper")  //映射包扫描
public class MybatisPlusConfig {
    //Mybatis_plus拦截器
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //建立插件核心类对象(拦截器)
        MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor();
        //建立乐观锁插件  对更新生效  并添加到核心插件对象实力里
        OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor 
                = new OptimisticLockerInnerInterceptor();
        plusInterceptor.addInnerInterceptor(optimisticLockerInnerInterceptor);
        //返回
        return plusInterceptor;
    }
}

而后在实体类上的version(名字随便)必定要加上@Version注解

4:防止全表更新与删除插件

  其实咱们在进行更新或者删除数据时可能会一不当心删除了所有,这都是由于咱们忘传条件,或者全匹配条件,这样就会形成数据的所有更新或者删除,这显然是不能够的,因此mybatis-plus为咱们提供了防止全表删除或者更新插件

 首先咱们去配置类配置 防止全表删除更新插件:

@SpringBootConfiguration    //声明为配置类
@MapperScan(basePackages = "cn.xw.mapper")  //映射包扫描
public class MybatisPlusConfig {

    //Mybatis_plus拦截器
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //建立插件核心类对象(拦截器)
        MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor();
        //建立乐观锁插件  对更新生效  并添加到核心插件对象实力里
        OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor
                = new OptimisticLockerInnerInterceptor();
        plusInterceptor.addInnerInterceptor(optimisticLockerInnerInterceptor);
        //建立 防止全表更新与删除插件(顺序必须在乐观锁插件后面)并添加到核心插件对象实力里
        BlockAttackInnerInterceptor blockAttackInnerInterceptor = new BlockAttackInnerInterceptor(); plusInterceptor.addInnerInterceptor(blockAttackInnerInterceptor); //返回
        return plusInterceptor;
    }
}
    //演示批量删除或者批量修改
    @Test
    void delTest() {
        int delete = studentMapper.delete(null);
        System.out.println("批量删除了:" + delete + " 条数据");
        // 防止全表更新与删除插件加上后执行批量删除
        //### The error occurred while executing an update  //解释:执行更新时发生错误
        //### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 
     //  Prohibition of full table deletion //解释禁止全表删除
    }
测试 防止全表删除更新插件

5:总结插件

  咱们在使用插件时和上述操做大同小异,直接建立指定插件并add添加到核心插件对象里便可

四:条件构造器

  条件构造器抽象类实际上是 AbstractWrapper 这个抽象类里面有特别多的条件方法,而它的实现类有 QueryWrapper 查询条件构造器和 UpdateWrapper 更新条件构造器,咱们在查询的时候就用QueryWrapper;在接下来的例子中我都以QueryWrapper实现类来讲明

具体条件构造器请参考官方文档

allEq:         多条件匹配查询所有      isNull:        字段 IS NULL                  
eq:            等于 =                    isNotNull:     字段 IS NOT NULL               
ne:            不等于 <>                 in:            字段 IN (v0, v1, ...)          
gt:            大于 >                    notIn:         字段 NOT IN (v0, v1, ...)      
ge:            大于等于 >=                or:            拼接 OR                       
lt:            小于 <                    andAND 嵌套                      
le:            小于等于 <=               inSql:          字段 IN ( sql语句 )           
betweenBETWEEN 值1 AND 值2       notInSql:       字段 NOT IN ( sql语句 )       
notBetween:    NOT BETWEEN 值1 AND 值2   groupBy:        分组:GROUP BY 字段, ...      
likeLIKE '%值%'               orderByAsc:     排序:ORDER BY 字段, ... ASC  
notLike:       NOT LIKE '%值%'           orderByDesc:    排序:ORDER BY 字段, ... DESC 
likeLeft:      LIKE '%值'                orderBy:        排序:ORDER BY 字段, ...      
likeRight:     LIKE '值%'                havingHAVING ( sql语句 )  
                                                                       
func:          func 方法(主要方便在出现if...else下调用不一样方法能不断链)
nested:        正常嵌套 不带 AND 或者 OR
apply:         拼接 sql
last:          无视优化规则直接拼接到 sql 的最后
exists:        拼接 EXISTS ( sql语句 )
notExists:     拼接 NOT EXISTS ( sql语句 )
AbstractWrapper条件查询方法

 1:QueryWrapper查询条件构造器

⭐ allEq  =  ;  eq = 

    allEq(Map<R, V> params) allEq(Map<R, V> params, boolean null2IsNull) allEq(boolean condition, Map<R, V> params, boolean null2IsNull) eq(R column, Object val) eq(boolean condition, R column, Object val)

    参数说明:
    Map<R, V> params :传入Map键值对 R 表字段 V 值
    boolean null2IsNull:传入的map键值对中,为true是,若是有键值为null的话是否以isNull查询
    是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段
    boolean condition:执行条件默认true,false 忽略咱们条件
    // 多条件查询匹配条件 allEq
    @Test
    void allEqTest() {
        //建立查询对象条件
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        //建立查询条件参数
        Map<String, Object> map = new HashMap<>();
        map.put("sage", 20);
        map.put("ssex", null);
        wrapper.allEq(true, map, true);
        List<Student> students = studentMapper.selectList(wrapper);
        students.forEach(System.out::println);
    }

    // 条件查询  等于 =
    @Test
    void eqTest() {
        //建立查询对象条件所有为男生数据 并查询
        List<Student> students = studentMapper
                .selectList(new QueryWrapper<Student>().eq("ssex", "男"));
        students.forEach(System.out::println);
    }
allEq、eq测试

⭐ ne <> ; gt > ; ge >= ; lt < ; le <= 

ne(R column, Object val) ne(boolean condition, R column, Object val) gt(R column, Object val) gt(boolean condition, R column, Object val) ge(R column, Object val) ge(boolean condition, R column, Object val) lt(R column, Object val) lt(boolean condition, R column, Object val) le(R column, Object val) le(boolean condition, R column, Object val)
 // 都差很少,以 gt为例
    // ne <> ; gt > ; ge >= ; lt < ; le <= 查询
    // .... FROM student WHERE (sage > ? AND ssex <> ?)
    @Test
    void gtTest(){
        List<Student> students = studentMapper
                .selectList(new QueryWrapper<Student>()
                        .gt("sage",21)
                        .ne("ssex","保密"));
        students.forEach(System.out::println);
    }
测试代码

 ⭐ 综合使用

 //综合编写
    @Test
    void sumTest(){
        //建立查询对象条件
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        //条件:查询学生id在5~15之间
        wrapper.between("sid",4,15);
        //条件:模糊查询学生中不带 %壮% 子的名字
        wrapper.notLike("sname","%壮%");
        List<Student> students = studentMapper.selectList(wrapper);
        students.forEach(System.out::println);
    }
综合操做

 五:执行 SQL 分析打印

  该功能依赖 p6spy 组件,能够完美输出打印SQL执行时常及其它信息

<!--性能测试-->
<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>3.9.1</version>
</dependency>

  更改配置application.yml配置:

spring:
  # 设置数据库链接信息
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql://localhost:3306/demo_m......
    .....

  在resources资源目录下建立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
spy.properties 配置文件

而后就和咱们正常使用同样来执行SQL语句查询等其它操做;在控制台会打印当前的运行sql执行时间等信息 以下打印:

Consume Time:17 ms 2021-04-12 19:37:25
Execute SQL:SELECT sid ...... FROM student WHERE (sid BETWEEN 4 AND 15 AND sname NOT LIKE '%%壮%%')

 六:代码生成器

  AutoGenerator 是 MyBatis-Plus 的代码生成器,经过 AutoGenerator 能够快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提高了开发效率

  在使用代码生成器以前咱们须要导入相应坐标

    <!--代码生成器坐标  我mybatis-plus版本是3.4.1-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!--下面两个根据本身选择的模板导入其中一个便可-->
        <!--模板坐标 freemarker-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>
        <!--模板坐标 velocity-engine-core-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>

在学习代码生成器以前资料的准备

代码生成器源码搜索下载  代码生成器具体配置 

public void fun() {
        System.out.println("java版本号:" + System.getProperty("java.version")); // java版本号
        System.out.println("Java提供商名称:" + System.getProperty("java.vendor")); // Java提供商名称
        System.out.println("Java提供商网站:" + System.getProperty("java.vendor.url")); // Java提供商网站
        System.out.println("jre目录:" + System.getProperty("java.home")); // Java,哦,应该是jre目录
        System.out.println("Java虚拟机规范版本号:" + System.getProperty("java.vm.specification.version")); // Java虚拟机规范版本号
        System.out.println("Java虚拟机规范提供商:" + System.getProperty("java.vm.specification.vendor")); // Java虚拟机规范提供商
        System.out.println("Java虚拟机规范名称:" + System.getProperty("java.vm.specification.name")); // Java虚拟机规范名称
        System.out.println("Java虚拟机版本号:" + System.getProperty("java.vm.version")); // Java虚拟机版本号
        System.out.println("Java虚拟机提供商:" + System.getProperty("java.vm.vendor")); // Java虚拟机提供商
        System.out.println("Java虚拟机名称:" + System.getProperty("java.vm.name")); // Java虚拟机名称
        System.out.println("Java规范版本号:" + System.getProperty("java.specification.version")); // Java规范版本号
        System.out.println("Java规范提供商:" + System.getProperty("java.specification.vendor")); // Java规范提供商
        System.out.println("Java规范名称:" + System.getProperty("java.specification.name")); // Java规范名称
        System.out.println("Java类版本号:" + System.getProperty("java.class.version")); // Java类版本号
        System.out.println("Java类路径:" + System.getProperty("java.class.path")); // Java类路径
        System.out.println("Java lib路径:" + System.getProperty("java.library.path")); // Java lib路径
        System.out.println("Java输入输出临时路径:" + System.getProperty("java.io.tmpdir")); // Java输入输出临时路径
        System.out.println("Java编译器:" + System.getProperty("java.compiler")); // Java编译器
        System.out.println("Java执行路径:" + System.getProperty("java.ext.dirs")); // Java执行路径
        System.out.println("操做系统名称:" + System.getProperty("os.name")); // 操做系统名称
        System.out.println("操做系统的架构:" + System.getProperty("os.arch")); // 操做系统的架构
        System.out.println("操做系统版本号:" + System.getProperty("os.version")); // 操做系统版本号
        System.out.println("文件分隔符:" + System.getProperty("file.separator")); // 文件分隔符
        System.out.println("路径分隔符:" + System.getProperty("path.separator")); // 路径分隔符
        System.out.println("直线分隔符:" + System.getProperty("line.separator")); // 直线分隔符
        System.out.println("操做系统用户名:" + System.getProperty("user.name")); // 用户名
        System.out.println("操做系统用户的主目录:" + System.getProperty("user.home")); // 用户的主目录
        System.out.println("当前程序所在目录:" + System.getProperty("user.dir")); // 当前程序所在目录
    }
System.getProperty("xx”);信息填写
package cn.xw;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

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

/**
 * @Auther: xiaoYang
 * @Date: 2021/4/13 14:29
 * @Description:
 */
public class GeneratorCodeUtils {
    public static void main(String[] args) {

        //设置做者信息
        String author = "xiaoYang";
        //数据库URL / username / password
        String url = "jdbc:mysql://localhost:3306/demo_mp?useSSL=true&useUnicode=true&" +
                "characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&" +
                "useLegacyDatetimeCode=false&serverTimezone=GMT%2B8";
        String username = "root";
        String password = "123";
        //这个include 和exclude只能存在一个
        String[] include = {"student"};
        String[] exclude = {};
        //乐观锁属性名称 和数据库表字段保持一致
        String version = "version";
        //逻辑删除属性名称 和数据库表字段保持一致
        String isDel = "is_del";

        //代码生成器
        AutoGenerator autoGenerator = new AutoGenerator();
        //全局相关配置
        GlobalConfig gc = new GlobalConfig();
        String propertyPath = System.getProperty("user.dir");
        gc.setOutputDir(propertyPath + "./src/main/java")
                .setAuthor(author)
                .setDateType(DateType.ONLY_DATE)
                .setEnableCache(false)
                .setBaseResultMap(true)
                .setBaseColumnList(true)
                .setOpen(false)
                .setServiceName("%sService");
        autoGenerator.setGlobalConfig(gc);
        //数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MYSQL);
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUrl(url);
        dsc.setUsername(username);
        dsc.setPassword(password);
        autoGenerator.setDataSource(dsc);
        //包配置
        PackageConfig pgc = new PackageConfig();
        pgc.setParent("cn.xw");
        autoGenerator.setPackageInfo(pgc);
        //自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
            }
        };
        String templatePath = "/templates/mapper.xml.ftl";
        ArrayList<FileOutConfig> focList = new ArrayList<>();
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 若是你 Entity 设置了先后缀、此处注意 xml 的名称会跟着发生变化!!
                return propertyPath + "/src/main/resources/mapper/" + pgc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        autoGenerator.setCfg(cfg);
        //模板信息配置
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setXml(null);
        autoGenerator.setTemplate(templateConfig);

        //其它信息配置
        //策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setInclude(include);
        //排除生成的表名称,就是写数据库表名称 String ... include
        strategy.setExclude(exclude);
        //strategy.setFieldPrefix("");
        strategy.setTablePrefix("");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        //表填充字段
        List<TableFill> fill = new ArrayList<>();
        fill.add(new TableFill("create_time", FieldFill.INSERT));
        fill.add(new TableFill("update_time", FieldFill.INSERT_UPDATE));
        strategy.setTableFillList(fill);
        strategy.setVersionFieldName(version);
        strategy.setLogicDeleteFieldName(isDel);
        autoGenerator.setStrategy(strategy);
        autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());
        autoGenerator.execute();
    }
}
若是只是单纯想快速生成,就复杂这里面代码测试跑通就能够了

代码生成器具体详细注释:

public static void main(String[] args) {
        //记得生成成功以后,在测试运行的时候要在主启动类上添加@MapperScan(value = “”)
        //##################    基本信息准备    ##################
        //获取当前程序所在目录 如:h://demo_mybatis_test
        String propertyPath = System.getProperty("user.dir");

        //建立代码生成器实例对象 (主要)
        AutoGenerator autoGenerator = new AutoGenerator();

        //##################   全局相关配置   ##################
        GlobalConfig gc = new GlobalConfig();
        //生成文件的输出目录【默认 D:\\ 盘根目录】
        gc.setOutputDir(propertyPath + "./src/main/java");
        //设置做者信息
        gc.setAuthor("xiaoYang");
        //时间类型对应策略
        // ONLY_DATE:使用 java.util.date 代替
        // SQL_PACK:使用 java.sql 包下的
        // TIME_PACK:使用 java.time 包下的 java8 新的时间类型
        gc.setDateType(DateType.ONLY_DATE);
        //是否在xml中添加二级缓存配置 默认false
        gc.setEnableCache(false);
        //是否在XML里建立 ResultMap开启 就是建立<resultMap></resultMap>标签  默认false
        gc.setBaseResultMap(true);
        //是否在XML里建立 columnList开启 就是建立<sql> 表所有字段 </sql>标签 默认false
        gc.setBaseColumnList(true);
        //是否打开输出目录 就是说建立完是否弹出window文件夹位置
        gc.setOpen(false);
        //!!!各层文件名称方式,注释的所有是默认的 例如: I%sService 生成 IStudentService
        //gc.setEntityName("%s");
        //gc.setMapperName("%sMapper");
        //gc.setXmlName("%sMapper");
        gc.setServiceName("%sService");
        //gc.setServiceImplName("%sServiceImpl");
        //gc.setControllerName("%sController");
        //实体属性 Swagger2 注解
        //gc.setSwagger2(true);
        // 把全局配置添加到 代码生成器实例里
        autoGenerator.setGlobalConfig(gc);

        //##################   数据源配置   ##################
        DataSourceConfig dsc = new DataSourceConfig();
        //数据库类型
        dsc.setDbType(DbType.MYSQL);
        //设置数据库驱动 此处是mysql8.0版本
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        // 数据库连接URL 我这里使用的是MySQL8的连接URL 同样的
        dsc.setUrl("jdbc:mysql://localhost:3306/demo_mp?useSSL=true&useUnicode=true&" +
                "characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&" +
                "useLegacyDatetimeCode=false&serverTimezone=GMT%2B8");
        // 设置数据库用户名
        dsc.setUsername("root");
        // 设置数据库密码
        dsc.setPassword("123");
        // 架构名称 默认就行
        //dsc.setSchemaName("public");
        //自定义类型转换 写这个方法是为了mybatis-plus根据数据库类型自动生成的实体类型不符合咱们要求是才自定义
        //当生成的model实体类,java类型不知足时能够自定义转换
        dsc.setTypeConvert(new ITypeConvert() {
            //数据库类型datetime默认生成的java类型为localDateTime, 下面示例要改为Date类型
            //类型转换(流程类型转换方法)
            @Override
            public IColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
                //获取数据库类型字符串转为小写
                String t = fieldType.toLowerCase();
                //判断当前获取的类型里是否包含"datetime" 成立的话就转类型返回
                if (t.contains("datetime")) {
                    System.out.println("类型捕捉datetime 将从LocalDateTime类型转Date");
                    return DbColumnType.DATE;
                }
                //其它字段采用默认转换(非mysql数据库可使用其它默认的数据库转换器)
                return new MySqlTypeConvert().processTypeConvert(globalConfig, fieldType);
            }
        });
        // 把数据源配置添加到 代码生成器实例里
        autoGenerator.setDataSource(dsc);

        //##################   包配置   ##################
        PackageConfig pgc = new PackageConfig();
        pgc.setParent("cn.xw")                  //父包名 默认"com.baomidou"
                .setPathInfo(null)              //路径配置信息,就是配置各个文件模板的路径信息
                .setModuleName("")              //父包模块名 默认 ”“
                .setEntity("entity")            //实体类包名 默认entity
                .setService("service")          //Service包名 默认”service“
                .setServiceImpl("service.impl") //Service里面的实现类位置   默认”service.impl“
                .setMapper("mapper")            //Mapper映射包名    默认”mapper“
                .setXml("mapper.xml")           //Mapper XML包名 默认"mapper.xml"
                .setController("controller");   //Controller包名  默认”controller“
        // 把包配置添加到 代码生成器实例里
        autoGenerator.setPackageInfo(pgc);

        //##################   自定义配置   ##################
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // 这里面能够设置具体的自定义表信息等等 正常不写 特殊百度学习
                //https://www.bookstack.cn/read/mybatis-3.3.2/spilt.7.4267953fc3057caf.md
            }
        };
        //注意 freemarker 和 velocity 使用哪一个模板就去导入哪一个坐标
        // 若是模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 若是模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";
        //自定义输出配置 就是说找到并定位到咱们生成的xxx(mapper).xml在哪里了
        ArrayList<FileOutConfig> focList = new ArrayList<>();
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 若是你 Entity 设置了先后缀、此处注意 xml 的名称会跟着发生变化!!
                return propertyPath + "/src/main/resources/mapper/" + pgc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        //设置自定义输出文件(就是上面几行咱们配置的)
        cfg.setFileOutConfigList(focList);
        // 把自定义配置添加到 代码生成器实例里
        autoGenerator.setCfg(cfg);

        //##################   模板信息配置   ##################
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setXml(null);
        //把模板信息配置添加到 代码生成器实例里
        autoGenerator.setTemplate(templateConfig);

        //##################   其它信息配置   ##################
        //策略配置
        StrategyConfig strategy = new StrategyConfig();
        //数据库表映射到实体类的命名策略 此时设置下划线转驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //数据库表字段映射到实体字段的命名策略  此时设置下划线转驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //setInclude和setExclude只能存在一个,不然某一个为空
        //setInclude必须写须要生成的数据库表
        //setExclude根据咱们链接的数据库,排除哪一个表不生产,其它全生成
        //须要生成的表名称,就是写数据库表名称 String ... include
        strategy.setInclude("student");
        //排除生成的表名称,就是写数据库表名称 String ... include
        //strategy.setExclude("teacher");
        //忽略表字段前缀
        strategy.setFieldPrefix("s");
        // 忽略表名的前缀
        strategy.setTablePrefix("");
        // 是否跳过视图
        strategy.setSkipView(true);
        //使用LomBok 默认false ,设置true注意要导入lombok坐标
        strategy.setEntityLombokModel(true);
        //生成的Controller层使用Rest风格 默认不使用
        strategy.setRestControllerStyle(true);
        //公共父类 若是你有本身的baseController的控制器能够写上
        //strategy.setSuperControllerClass("你本身的父类控制器,没有就不用设置!");
        // 写于父类中的公共字段
        strategy.setSuperEntityColumns("id");
        //表填充字段
        List<TableFill> fill = new ArrayList<>();
        fill.add(new TableFill("create_time", FieldFill.INSERT));
        fill.add(new TableFill("update_time", FieldFill.INSERT_UPDATE));
        strategy.setTableFillList(fill);
        //乐观锁属性名称
        strategy.setVersionFieldName("version");
        //逻辑删除属性名称
        strategy.setLogicDeleteFieldName("is_del");
        
        //把策略配置添加到 代码生成器实例里
        autoGenerator.setStrategy(strategy);
        //设置那种模板引擎
        autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());
        // 执行生成
        autoGenerator.execute();
    }

.

相关文章
相关标签/搜索