04-MybatisPlus篇

概述

需要的基础:把我的MyBatis、Spring、SpringMVC就可以学习这个了!

为什么要学习它呢?MyBatisPlus可以节省我们大量工作时间,所有的CRUD代码它都可以自动化完成!

  • 狂神说-MybatisPlus视频

简介

MyBatis 本来就是简化 JDBC 操作的!

MyBatis Plus,简化 MyBatis !

  • 官方:https://baomidou.com/

image-20220114192304418

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作, BaseMapper

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求, 以后简单的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 操作智能分析阻断,也可自定义拦截规则,预防误 操作

快速入门

  • 教程:https://baomidou.com/pages/226c21/

步骤

  1. 创建数据库mybatis_plus

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

  1. 编写项目,初始化项目!使用SpringBoot初始化!

image-20220117202508799
image-20220117202604139
image-20220117210257734
  1. 导入依赖

mybatis-plus可以节省大量的代码,mybatis与mybatis-plus尽量不要同时导入,会存在版本差异。

  1. 连接数据库!这一步和 mybatis 相同!

image-20220117211906278
  1. 传统方式pojo、dao(链接mybatis,配置mapper.xml)、service、controller

  2. 使用了mybatis-plus之后

    • pojo

    • mapper接口

    最好使用@Mapper进行注册,后面就不需要使用@MapperScan

    • 注意点,需要在主启动类上去扫描我们的mapper包下的所有接口

    在mapper层使用@Mapper就不需要扫描了

    • 测试类中测试

image-20220209000050375

配置日志

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

image-20220209000309323

CRUD操作

插入操作

image-20220209000807330

数据库插入的id的默认值为:全局的唯一id

主键生成策略

默认ID_WORKER全局唯一id

分布式系统唯一id生成:https://www.cnblogs.com/haoxinyue/p/5208136.html

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

主键自增

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

  • 修改主键字段id为自增,数据库字段一定要是自增!

image-20220209002258304
  • 测试

image-20220209002415444
  • IdType其他类型字段

自定义ID生成器

更新操作

  • updateById(Object):根据id进行更新,参数为对象

image-20220209003034209
  • update(Object,Wrapper)根据指定Wrapper参数进行更新Object

image-20220209004439728

自动填充

创建时间、修改时间!这些个操作一遍都是自动化完成的,我们不希望手动更新!

阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified几乎所有的表都要配置上!而且需 要自动化!

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

  • 在表中新增字段 create_time, update_time

image-20220209005015060
  • 再次测试插入方法,我们需要先把实体类同步!

  • 再次更新查看结果

image-20220209005200817

方式二:代码级别

  • 删除数据库的默认值、更新操作!

image-20220209010508716
  • 实体类字段属性上需要增加注解

  • 编写处理器来处理这个注解即可!

创建handler包,在包下面创建MyMetaObjectHandler.java

  • 测试插入

  • 测试更新、观察时间即可!

image-20220209010532704

乐观锁

在面试过程中,我们经常会被问道乐观锁,悲观锁!这个其实非常简单!

乐观锁 : 故名思意十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题, 再次更新值测试

悲观锁:故名思意十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作!

这里主要讲解 乐观锁机制!

乐观锁实现方式:

  • 取出记录时,获取当前 version

  • 更新时,带上这个version

  • 执行更新时, set version = newVersion where version = oldVersion

  • 如果version不对,就更新失败

  1. 给数据库中增加version字段!

image-20220209010941335
  1. 实体类加对应的字段

  1. 注册组件

  • 测试一下!

  1. 测试乐观锁成功

  1. 测试乐观锁失败!

image-20220209012440209

查询操作

  • 根据id查询

  • 根据多个id查询

  • 根据map条件查询

分页操作

  • 原始的 limit 进行分页

  • pageHelper 第三方插件

  • 内置分页插件

  1. MybatisPlusConfig.java中添加分页插件

  • 直接使用Page对象即可!

  1. 默认分页

  1. 根据wrapper条件分页

删除操作

  • 根据id删除记录

  • 通过id批量删除

  • 通过map删除

在工作中会遇到一些问题:逻辑删除!

逻辑删除

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

逻辑删除 :再数据库中没有被移除,而是通过一个变量来让他失效! deleted = 0 => deleted = 1

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

image-20220209014952154
  • 实体类中增加相应属性

  • MybatisPlusConfig.java中配置逻辑删除组件

高版本不用配置插件了,上述java代码无需编写。

  • 配置参数

  • 测试下删除

image-20220209015842535

发现,现在没有实行删除了,而是进行了更新操作。

性能分析插件

我们在平时的开发中,会遇到一些慢sql。测试! druid…..

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

MP也提供性能分析插件,如果超过这个时间就停止运行!

  • 导入插件

  • application.properties配置

  • 添加 spy.properties配置文件,在resources目录下

  • 运行查询测试

image-20220209021443611

条件构造器

十分重要:Wrapper

写一些复杂的sql就可以使用它来替代!

AbstractWrapper

QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类 用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件 注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

allEq

  • 全部eq(或个别isNull)

    params : key为数据库字段名,value为字段值 null2IsNull : 为true则在mapvaluenull时调用 isNull 方法,为false时则忽略valuenull

  • 例1: allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null

  • 例2: allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'

个别参数说明:

filter : 过滤函数,是否允许字段传入比对条件中 paramsnull2IsNull : 同上

  • 例1: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null})--->name = '老王' and age is null

  • 例2: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null}, false)--->name = '老王'

eq

  • 等于 =

  • 例: eq("name", "老王")--->name = '老王'

ne

  • 不等于 <>

  • 例: ne("name", "老王")--->name <> '老王'

gt

  • 大于 >

  • 例: gt("age", 18)--->age > 18

ge

  • 大于等于 >=

  • 例: ge("age", 18)--->age >= 18

lt

  • 小于 <

  • 例: lt("age", 18)--->age < 18

le

  • 小于等于 <=

  • 例: le("age", 18)--->age <= 18

between

  • BETWEEN 值1 AND 值2

  • 例: between("age", 18, 30)--->age between 18 and 30

notBetween

  • NOT BETWEEN 值1 AND 值2

  • 例: notBetween("age", 18, 30)--->age not between 18 and 30

like

  • LIKE '%值%'

  • 例: like("name", "王")--->name like '%王%'

notLike

  • NOT LIKE '%值%'

  • 例: notLike("name", "王")--->name not like '%王%'

likeLeft

  • LIKE '%值'

  • 例: likeLeft("name", "王")--->name like '%王'

likeRight

  • LIKE '值%'

  • 例: likeRight("name", "王")--->name like '王%'

isNull

  • 字段 IS NULL

  • 例: isNull("name")--->name is null

isNotNull

  • 字段 IS NOT NULL

  • 例: isNotNull("name")--->name is not null

in

  • 字段 IN (value.get(0), value.get(1), ...)

  • 例: in("age",{1,2,3})--->age in (1,2,3)

  • 字段 IN (v0, v1, ...)

  • 例: in("age", 1, 2, 3)--->age in (1,2,3)

notIn

  • 字段 NOT IN (value.get(0), value.get(1), ...)

  • 例: notIn("age",{1,2,3})--->age not in (1,2,3)

  • 字段 NOT IN (v0, v1, ...)

  • 例: notIn("age", 1, 2, 3)--->age not in (1,2,3)

inSql

  • 字段 IN ( sql语句 )

  • 例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)

  • 例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)

notInSql

  • 字段 NOT IN ( sql语句 )

  • 例: notInSql("age", "1,2,3,4,5,6")--->age not in (1,2,3,4,5,6)

  • 例: notInSql("id", "select id from table where id < 3")--->id not in (select id from table where id < 3)

groupBy

  • 分组:GROUP BY 字段, ...

  • 例: groupBy("id", "name")--->group by id,name

orderByAsc

  • 排序:ORDER BY 字段, ... ASC

  • 例: orderByAsc("id", "name")--->order by id ASC,name ASC

orderByDesc

  • 排序:ORDER BY 字段, ... DESC

  • 例: orderByDesc("id", "name")--->order by id DESC,name DESC

orderBy

  • 排序:ORDER BY 字段, ...

  • 例: orderBy(true, true, "id", "name")--->order by id ASC,name ASC

having

  • HAVING ( sql语句 )

  • 例: having("sum(age) > 10")--->having sum(age) > 10

  • 例: having("sum(age) > {0}", 11)--->having sum(age) > 11

func

  • func 方法(主要方便在出现if...else下调用不同方法能不断链)

  • 例: func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})

or

  • 拼接 OR

    注意事项:

    主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)

  • 例: eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'

  • OR 嵌套

  • 例: or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')

and

  • AND 嵌套

  • 例: and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')

nested

  • 正常嵌套 不带 AND 或者 OR

  • 例: nested(i -> i.eq("name", "李白").ne("status", "活着"))--->(name = '李白' and status <> '活着')

apply

  • 拼接 sql

    注意事项:

    该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!

  • 例: apply("id = 1")--->id = 1

  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

last

  • 无视优化规则直接拼接到 sql 的最后

    注意事项:

    只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

  • 例: last("limit 1")

exists

  • 拼接 EXISTS ( sql语句 )

  • 例: exists("select id from table where age = 1")--->exists (select id from table where age = 1)

notExists

  • 拼接 NOT EXISTS ( sql语句 )

  • 例: notExists("select id from table where age = 1")--->not exists (select id from table where age = 1)

QueryWrapper

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件 及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取

select

  • 设置查询字段

    说明:

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

  • 例: select("id", "name", "age")

  • 例: select(i -> i.getProperty().startsWith("test"))

UpdateWrapper

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件 及 LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

set

  • SQL SET 字段

  • 例: set("name", "老李头")

  • 例: set("name", "")--->数据库字段值变为空字符串

  • 例: set("name", null)--->数据库字段值变为null

setSql

  • 设置 SET 部分 SQL

  • 例: setSql("name = '老李头'")

lambda

  • 获取 LambdaWrapperQueryWrapper中是获取LambdaQueryWrapperUpdateWrapper中是获取LambdaUpdateWrapper

使用 Wrapper 自定义SQL

需要mybatis-plus版本 >= 3.0.7 param 参数名要么叫ew,要么加上注解@Param(Constants.WRAPPER) 使用${ew.customSqlSegment} 不支持 Wrapper 内的entity生成where语句

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

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

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

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

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

用注解

用XML

kotlin使用wrapper

kotlin 可以使用 QueryWrapperUpdateWrapper 但无法使用 LambdaQueryWrapperLambdaUpdateWrapper 如果想使用 lambda 方式的 wrapper 请使用 KtQueryWrapperKtUpdateWrapper

请参考实例(opens new window)

链式调用 lambda 式

代码自动生成器

dao、pojo、service、controller都给我自己去编写完成!

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、 Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

导入依赖

plus代码生成器需要一个模板引擎,velocity和freemarker任选一个,velocity是生成器中默认使用的,根据你的选择引依赖。

写一个构造器类

生成模板

官方快速生成模板

官方交互式生成模板

代码配置

  • 运行

最后更新于

这有帮助吗?