> 文档中心 > 一文速通MybatisPlus

一文速通MybatisPlus

目录

  Mybatis-plus简介

MP特性

框架的结构

 MP快速入门

 配置日志输出

主键生成策略

 自动填充

方式一:数据库级别

方式二:代码控制级别(常用)

乐观锁和悲观锁

测试乐观锁单线程成功的例子 

 乐观锁多线程失败案例

查询操作

分页查询

 删除操作

删除单个

 删除多个

 按map删除

逻辑删除

 性能分析插件p6spy

条件查询器wrapper

 isNotNull匹配不为空的

 eq匹配相等

 between查询区间

like模糊匹配

 insql子查询

 orderByDesc排序

 代码自动生成器


  Mybatis-plus简介

mybatis-plus官网

 r

mybatis-plus(简称MP)是一个mybatis的增强工具,在Mybatis的基础上自作增强不做改变,为简化开发、提高效率而生。Mybatis-plus提供了通用的mapper和service,可以在不便携任何sql语句的情况下,快速的实现对单表的crud、批量、逻辑删除、分页操作。

MP特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

框架的结构

 MP快速入门

新建一个数据表

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

插入一些数据

INSERT INTO user (id, name, age, email) VALUES(1, 'Jone', 18, 'test1@1.com'),(2, 'Jack', 20, 'test2@2.com'),(3, 'Tom', 28, 'test3@3.com'),(4, 'Sandy', 21, 'test4@4.com'),(5, 'Billie', 24, 'test5@5.com');

idea中 新建一个模块。

pom.xml中

        com.baomidou     mybatis-plus-boot-starter     3.4.2       mysql     mysql-connector-java     runtime       org.projectlombok     lombok     true       org.springframework.boot     spring-boot-starter-test     test             org.junit.vintage      junit-vintage-engine            

mapper类下

package com.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.pojo.User;import org.springframework.stereotype.Component;@Componentpublic interface UserMapper  extends BaseMapper {}

pojo包下

import lombok.Data;@Datapublic class User { private Long id; private String name; private Integer age; private String email;}

application.yaml配置文件下

#配置相关信息spring:  datasource:    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=UTC    username: root    password: 123456

测试类

import com.mapper.UserMapper;import com.pojo.User;import org.junit.jupiter.api.Test;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest@MapperScan("com.mapper")//扫描mapper,自动创建实现类class MybatisPlusQuickApplicationTests {    @Autowired    private UserMapper userMapper;    @Test    void contextLoads() { List users = userMapper.selectList(null); users.forEach(System.out::println);    }}

运行结果

 配置日志输出

在控制台中想要知道mp的执行过程,在配置文件配置

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

运行结果 

 由此可见,mp的底层还是调用了jdbc去创建SqlSession对象。

主键生成策略

  • 数据库自增长
  • UUID
  • Redis生成id
  • snowflake(雪花算法)

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0

mybatisplus的主键生成策略就是采用这种雪花算法方式

测试插入

    @Test    void insert() { User user = new User(); user.setName("kc"); user.setAge(18); user.setEmail("123@qq.com"); userMapper.insert(user);    }

 配置主键自增

在实体类对象中配置,前提是数据库表中也是设置自增的

@Datapublic class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email;}

IdType源码

public enum IdType {    AUTO(0),数据库id自增    NONE(1),未设置主键    INPUT(2),手动输入    ID_WORKER(3),默认的全局唯一id    ID_WORKER_STR(3), ID_WORKER字符串表示法    UUID(4);//全局唯一id uuid}

自动拼接动态sql 

 自动填充

创建时间、修改时间,这些操作一般都是自动化完成的,不希望手动更新。

阿里巴巴开发手册:一般所有的数据库表:都要配上gmt_create、gmt_modified这两个字段,而且需要自动化。

方式一:数据库级别

在数据库表中新增这两个字段,默认值为CURRENT_TIMESTAMP,表示当前时间

方式二:代码控制级别(常用)

数据库表通常是不能够修改的,在代码中使用自动填充

首先在对象上添加   @TableField字段

import java.util.Date;@Datapublic class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; @TableField(fill = FieldFill.INSERT)//插入的时候执行      private Date creatTime; @TableField(fill = FieldFill.INSERT_UPDATE)//插入和更新时执行      private Date updateTime;}

然后自定义实现类 MyMetaObjectHandler,好让spring识别并做出处理

package com.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.util.Date;@Slf4j@Component//得交给spring容器管理public class MyMetaObjectHandler implements MetaObjectHandler {    @Override    public void insertFill(MetaObject metaObject) {      log.info("start insert fill...");      //setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)      this.setFieldValByName("creatTime", new Date(),metaObject);      this.setFieldValByName("updateTime", new Date(),metaObject);    }    @Override    public void updateFill(MetaObject metaObject) { log.info("start update fill..."); this.setFieldValByName("updateTime", new Date(),metaObject);    }}

测试插入

    @Test    void insert() { User user = new User(); user.setName("autoInertTest"); user.setAge(123); user.setEmail("12133@qq.com"); userMapper.insert(user);    }

运行结果 

测试更新 

    @Test    void update() { User user = new User(); user.setId(5L); user.setName("kongchaoAfter"); user.setAge(1222); user.setEmail("nono@qq.com"); userMapper.updateById(user);    }

 运行结果

乐观锁和悲观锁

乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不去上锁,如果出现了问题,在次更新值测试

在数据库中新增字段version,在实体类中也要增加对应的,使用@Version表示他是一个乐观锁

乐观锁:先查询,获取它的版本号,然后在一系列的操作后,让他的版本号+1,前提是这个版本号还是之前查出来的那个版本号。

 @Version//乐观锁private Integer version;

 写一个配置类

@Configuration@MapperScan("com.mapper")public class MyBatisPlusConfig {    @Bean    public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor;    }}

测试乐观锁单线程成功的例子 

 更新下

    @Test    void update() { User user = userMapper.selectById(5L); user.setName("kongchaoAfter"); user.setAge(21); user.setEmail("kc@qq.com"); userMapper.updateById(user);    }

 说明:

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

 

 乐观锁多线程失败案例

@Test    void update2() { //线程1 User user = userMapper.selectById(5L); user.setName("kongchao1"); user.setAge(11); user.setEmail("kc1@qq.com"); //模拟另一个线程插队 User user2 = userMapper.selectById(5L); user2.setName("kongchao2"); user2.setAge(22); user2.setEmail("kc2@qq.com"); userMapper.updateById(user2); //若没有乐观锁,则会覆盖user2的操作 userMapper.updateById(user);    }

数据库 

 运行结果

查询操作

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

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

@SpringBootTest@MapperScan("com.mapper")//扫描mapper,自动创建实现类class MybatisPlusQuickApplicationTests {    @Autowired    private UserMapper userMapper;     //按条件查询之一使用map操作     @Test     public void testSelectByBatchIds(){  HashMap map = new HashMap();  //  自定义要查询  map.put("name","kc");  map.put("age",18);  List users = userMapper.selectByMap(map);  users.forEach(System.out::println);     }}

分页查询

在配置类中加入分页插件

@Configuration@MapperScan("com.mapper")public class MyBatisPlusConfig {  //  分页插件    @Bean    public PaginationInterceptor paginationInterceptor(){ return  new PaginationInterceptor();    }}

在测试类中测试

     //测试分页查询    @Test    public void pageTest(){ //第一页,每页三个数据Page page=new Page(1, 3); userMapper.selectPage(page, null); page.getRecords().forEach(System.out::print);    }

可以获取各种参数 

运行结果

 删除操作

删除单个

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

 删除多个

    //删除多个    @Test    public void deleteByIds(){ userMapper.deleteBatchIds(Arrays.asList(1583366844257988612L,1583366844257988613L));    }

 按map删除

    //通过map删除@Test    public void deleteMap(){ HashMap hashMap=new HashMap(); hashMap.put("name", "kc"); userMapper.deleteByMap(hashMap);}

逻辑删除

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

逻辑删除:数据还存在数据库中,是增加了一个deleted字段,以这个字段作为条件,当字段=1时不显示这条数据

这个功能类似于回收站。并没有真正删除。

在数据库中添加字段deleted然后在实体类中也加入,并带上注解

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

 在application.yaml配置文件中配置

mybatis-plus:  global-config:    db-config:#      没有逻辑删除的显示为0,否则显示1      logic-delete-value: 1      logic-not-delete-value: 0

测试之前的删除

   //逻辑删除单个    @Test    public void deleteById(){ userMapper.deleteById(6L);    }

 运行结果,删除只是将deleted字段变为1

 测试查询

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

运行结果,发现也会加上deleted=0的条件

 性能分析插件p6spy

在平时的使用中会遇到一些比较慢的sql

作用:性能分析拦截器,用于输出每条 SQL 语句及其执行时间 p6spy原理 p6spy将应用的数据源给劫持了,应用操作数据库其实在调用p6spy的数据源,p6spy劫持到需要执行的sql或者hql之类的语句之后,他自己去调用一个realDatasource,再去操作数据库

pom.xml导入p6spy坐标

      p6spy     p6spy     3.9.0 

application.yaml配置文件中

#配置相关信息spring:  datasource:#    driver-class-name: com.mysql.cj.jdbc.Driver    driver-class-name: com.p6spy.engine.spy.P6SpyDriver#    url:  jdbc:p6spy:mysql://localhost:3306/mybatisplus?useSSL=false&serverTimezone=GMT%2B8#    url: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=UTC    username: root    password: 123456

在resource新建spy.properties

driverlist=com.mysql.cj.jdbc.DriverlogMessageFormat=com.p6spy.engine.spy.appender.MultiLineFormat#logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormatdatabaseDialectDateFormat=yyyy-MM-dd HH:mm:ssappender=com.p6spy.engine.spy.appender.StdoutLogger

测试代码

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

运行结果

条件查询器wrapper

官网查看mp条件构造器

 isNotNull匹配不为空的

@SpringBootTestpublic class TestWrapper {    @Autowired    private UserMapper userMapper;    @Test    public void test1(){ QueryWrapper wrapper=new QueryWrapper(); //查询name字段不为空,且年龄大于等于15的 //wrapper返回的还是对象,所以可以直接.操作 wrapper.isNotNull("name").ge("age", 15); userMapper.selectList(wrapper);    }}

运行结果,条件构造器的本质就是省sql语句的编写,可以用来做些判断

 eq匹配相等

 @Test    public void test2(){ QueryWrapper wrapper=new QueryWrapper(); //查询name为kongchao的 wrapper.eq("name", "kongchao");    userMapper.selectOne(wrapper);    }

 between查询区间

  @Test    public void test3(){ QueryWrapper wrapper=new QueryWrapper(); //查询年龄在10-18岁的(闭区间) wrapper.between("age", 10, 18); userMapper.selectList(wrapper);    }

like模糊匹配

    @Test    public void test4(){ QueryWrapper wrapper=new QueryWrapper(); //名字中不含o,邮箱是以t开头 (t%) wrapper.notLike("name", "o").likeRight("email", "t"); userMapper.selectList(wrapper);    }

 insql子查询

    @Test    public void test5(){ QueryWrapper wrapper=new QueryWrapper();wrapper.inSql("id", "select id from user where age>=20"); userMapper.selectList(wrapper);    }

 orderByDesc排序

    @Test    public void test6(){ //id降序排序 QueryWrapper wrapper=new QueryWrapper();wrapper.orderByDesc("id"); userMapper.selectList(wrapper);    }

 代码自动生成器

pom.xml下

         com.baomidou     mybatis-plus-generator     3.4.1       org.apache.velocity     velocity-engine-core     2.0       com.alibaba     fastjson     1.2.47       com.baomidou     mybatis-plus-boot-starter     3.4.2       p6spy     p6spy     3.9.0       com.alibaba     druid     1.2.12       log4j     log4j     1.2.17       io.springfox     springfox-swagger-ui     2.7.0       org.springframework.boot     spring-boot-starter-thymeleaf       org.springframework.boot     spring-boot-devtools       org.springframework.boot     spring-boot-configuration-processor       mysql     mysql-connector-java     runtime       org.projectlombok     lombok     true       org.springframework.boot     spring-boot-starter-test     test             org.junit.vintage      junit-vintage-engine            

自动生成的代码

package conf;import com.baomidou.mybatisplus.generator.config.GlobalConfig;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.annotation.FieldFill;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.generator.AutoGenerator;import com.baomidou.mybatisplus.generator.config.DataSourceConfig;import com.baomidou.mybatisplus.generator.config.PackageConfig;import com.baomidou.mybatisplus.generator.config.StrategyConfig;import com.baomidou.mybatisplus.generator.config.po.TableFill;import com.baomidou.mybatisplus.generator.config.rules.DateType;import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;import java.util.ArrayList;public class AutoMP {    // 代码自动生成器   public static void main(String[] args) {    // 需要构建一个 代码自动生成器 对象 AutoGenerator mpg = new AutoGenerator(); // 配置策略 // 1、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath+"/src/main/java"); gc.setAuthor("执久呀"); gc.setOpen(false); gc.setFileOverride(false); // 是否覆盖 gc.setServiceName("%sService"); // 去Service的I前缀 gc.setIdType(IdType.ID_WORKER); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true); mpg.setGlobalConfig(gc); //2、设置数据源 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("123456");dsc.setDbType(DbType.MYSQL);mpg.setDataSource(dsc);//3、包的配置 PackageConfig pc = new PackageConfig();pc.setModuleName("blog");pc.setParent("autopage");pc.setEntity("pojo");pc.setMapper("mapper");pc.setService("service");pc.setController("controller");mpg.setPackageInfo(pc);//4、策略配置StrategyConfig strategy = new StrategyConfig();strategy.setInclude("user"); // 设置要映射的表名strategy.setNaming(NamingStrategy.underline_to_camel);strategy.setColumnNaming(NamingStrategy.underline_to_camel);strategy.setEntityLombokModel(true); // 自动lombok;strategy.setLogicDeleteFieldName("deleted");// 自动填充配置TableFill gmtCreate = new TableFill("create_time", FieldFill.INSERT);TableFill gmtModified = new TableFill("update_time",FieldFill.INSERT_UPDATE);ArrayList tableFills = new ArrayList();tableFills.add(gmtCreate);tableFills.add(gmtModified);strategy.setTableFillList(tableFills);// 乐观锁strategy.setVersionFieldName("version");strategy.setRestControllerStyle(true);strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_2mpg.setStrategy(strategy);mpg.execute(); //执行     }}

31戒烟网