刘頔

Mar 31, 2023

MyBatis-Plus详解

前言

在介绍MyBatis-Plus之前还是要先简单介绍一下MyBatis
MyBatis是一个Java持久化框架,他可以简化实现MySQL操作
  • SQL语句的编写:MyBatis将SQL语句和Java代码解耦,使用XML文件或注解编写SQL语句,降低了代码的耦合度。
  • 对象关系映射(ORM):MyBatis通过配置文件或者注解将Java对象与数据库表进行映射,从而实现Java对象与数据库表的关联
  • 数据库连接管理:Mybatis使用数据源来管理数据库连接,提高了数据库的访问效率。同时可以自定义连接池大小、超时时间等参数,以适应不同场景下的需求。
  • 分页查询:Mybatis提供了分页查询的支持,可以通过配置文件或注解来实现分页查询。
总的来说,Mybatis在MySQL操作上的简化体现在SQL语句编写、ORM、连接管理、分页查询、动态SQL支持等方面,提高了程序开发效率,降低了开发难度。

那么我们来介绍下连接池

连接池:

MyBatis连接池是一个用于管理数据库连接的对象池。当应用程序需要连接到数据库时,它可以从连接池中获取可用的连接,而不必每次都创建新的连接。这可以提高应用程序的性能和可伸缩性。
连接池通常在应用程序启动时初始化,并在应用程序关闭时销毁。连接池的大小取决于应用程序的需求和底层系统的资源限制。如果连接池已满,应用程序仍需要连接,则会阻塞直到有可用连接或等待超时。
MyBatis连接池定义了一组规则来管理连接,例如最大空闲时间、最大活动连接数以及连接回收策略等。这些规则可以通过配置文件进行设置。
MyBatis连接池具有各种可配置的属性,以下是一些常用的配置选项:
  1. driver:指定数据库驱动类名。
  1. url:指定数据库连接字符串。
  1. username:指定数据库用户名。
  1. password:指定数据库密码。
  1. initialSize:连接池中初始化连接数。
  1. maxActive:连接池中最大活动连接数。
  1. maxIdle:连接池中最大空闲连接数。
  1. minIdle:连接池中最小空闲连接数。
  1. maxWait:获取连接的最大等待时间(毫秒)。
  1. timeBetweenEvictionRunsMillis:连接回收器线程运行间隔时间。
  1. minEvictableIdleTimeMillis:连接在池中保持空闲而不被使用的最短时间。
  1. testWhileIdle:是否开启空闲连接检测。
  1. testOnBorrow:是否开启获取连接时的检测。
  1. testOnReturn:是否开启归还连接时的检测。
  1. validationQuery:用来检验连接是否有效的 SQL 语句。
  1. removeAbandoned:是否移除废弃的连接。
  1. removeAbandonedTimeout:废弃连接超时时间。
这些配置选项可以通过在 MyBatis 配置文件中添加 <dataSource> 元素来设置。例如:
<dataSource type="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="user" value="root"/> <property name="password" value="root"/> <property name="initialPoolSize" value="5"/> <property name="maxPoolSize" value="10"/> <property name="minPoolSize" value="2"/> <property name="maxIdleTime" value="3600"/> </dataSource>

MyBatis-Plus简介

ok,我们言归正传开始介绍MyBatis-Plus相关内容。
MyBatis-Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为进化开发,提高效率而生。没错MyBatis在传统JDBC的基础上做了简化,而MyBatis-Plus又在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 操作智能分析阻断,也可自定义拦截规则,预防误操作

框架结构

notion image

如何使用

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

注解

属性
类型
必须指定
默认值
描述
value
String
""
表名
schema
String
""
schema
keepGlobalPrefix
boolean
false
是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时)
resultMap
String
""
xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定)
autoResultMap
boolean
false
是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入)
excludeProperty
String[]
{}
需要排除的属性名 @since 3.3.1
属性
类型
必须指定
默认值
描述
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)
备注:这个值也可以在配置文件中设置默认值
属性
类型
必须指定
默认值
描述
value
String
""
数据库字段名
exist
boolean
true
是否为数据库表字段
condition
String
""
字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s}参考(opens new window)
update
String
""
字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性)
insertStrategy
Enum
FieldStrategy.DEFAULT
举例:NOT_NULLinsert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
updateStrategy
Enum
FieldStrategy.DEFAULT
举例:IGNOREDupdate table_a set column=#{columnProperty}
whereStrategy
Enum
FieldStrategy.DEFAULT
举例:NOT_EMPTYwhere <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
fill
Enum
FieldFill.DEFAULT
字段自动填充策略
select
boolean
true
是否进行 select 查询
keepGlobalFormat
boolean
false
是否保持使用全局的 format 进行处理
jdbcType
JdbcType
JdbcType.UNDEFINED
JDBC 类型 (该默认值不代表会按照该值生效)
typeHandler
Class<? extends TypeHandler>
UnknownTypeHandler.class
类型处理器 (该默认值不代表会按照该值生效)
numericScale
String
""
指定小数点后保留的位数
  • @Version
    • 描述:乐观锁注解、标记 @Version 在字段上
  • @EnumValue
    • 描述:普通枚举类注解(注解在枚举字段上)
描述:表字段逻辑处理注解(逻辑删除)
属性
类型
必须指定
默认值
描述
value
String
""
逻辑未删除值
delval
String
""
逻辑删除值
  • @KeySequence
    • 描述:序列主键策略 oracle
    • 属性:value、dbType
    • 属性
      类型
      必须指定
      默认值
      描述
      value
      String
      ""
      序列名
      dbType
      Enum
      DbType.OTHER
      数据库类型,未配置默认使用注入 IKeyGenerator 实现,多个实现必须指定
  • 描述:内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
属性
类型
必须指定
默认值
描述
isDesc
boolean
true
是否倒序查询
sort
short
Short.MAX_VALUE
数字越小越靠前

MyBatis-Plus分页原理

MyBatis-Plus 分页采用了分页插件来实现。
分页插件的原理是在查询语句执行前,先通过拦截器将原始 SQL 语句改造成对应的分页 SQL 语句,并在其中添加分页限制条件,然后再执行查询操作。分页插件会通过 Page 对象来控制分页信息,Page 对象包含了分页参数(当前页、每页显示数量)以及分页结果(总记录数、总页数、分页数据列表)等信息。
MyBatis-Plus 的分页插件提供了两种方式来实现分页:
  1. 基于 RowBounds 的分页
基于 RowBounds 实现的分页,即是查询时指定一个 RowBounds 对象,该对象包含了分页的信息,MyBatis-Plus 会拦截这个查询并进行改造,然后再执行查询操作。这种方式的优点是可以使用多种数据库的分页机制,但缺点就是需要手动传入 RowBounds 对象。
  1. 基于 PageHelper 的分页
基于 PageHelper 实现的分页,使用起来非常方便,只需要在查询方法中传入一个 Page 对象即可。MyBatis-Plus 会自动拦截该查询,并进行分页改造。这种方式的优点是使用起来非常简单,但缺点就是只能使用一种数据库的分页机制。

MyBatis-Plus插件

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

属性

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

InnerInterceptor

我们提供的插件都将基于此接口来实现功能
目前已有的功能:
  • 自动分页: PaginationInnerInterceptor
  • 多租户: TenantLineInnerInterceptor
  • 动态表名: DynamicTableNameInnerInterceptor
  • 乐观锁: OptimisticLockerInnerInterceptor
  • sql 性能规范: IllegalSQLInnerInterceptor
  • 防止全表更新与删除: BlockAttackInnerInterceptor

使用方式:

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

乐观锁

当要更新一条记录的时候,希望这条记录没有被别人更新乐观锁实现方式:
取出记录时,获取当前 version更新时,带上这个 version执行更新时, set version = newVersion where version = oldVersion如果 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 不能复用!!!

Copyright © 2025 刘頔

logo