MyBatis-Plus时Mybatis的Best Partner
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
#支持数据库
引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 mybatis-spring-boot-starter和MyBatis-Spring,以避免因版本差异导致的问题
描述:表名注解,标识实体类对应的表
使用位置:实体类
注意: 默认是以对象名为表名,可以配置全局配置名字增加前缀,
也可以用@TableName进行注释表名,如下(同时配置,注解生效)
。@TableName("sys_user") public class User { private Long id; private String name; private Integer age; private String email; }
属性 |
类型 |
必须指定 |
默认值 |
描述 |
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 |
关于 autoResultMap 的说明:
MP 会自动构建一个 resultMap 并注入到 MyBatis 里(一般用不上),请注意以下内容:
因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 Entity 字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。
而对于 typeHandler 属性,MyBatis 只支持写在 2 个地方:
除了以上两种直接指定 typeHandler 的形式,MyBatis 有一个全局扫描自定义 typeHandler 包的配置,原理是根据您的 property 类型去找其对应的 typeHandler 并使用。
描述:主键注解
使用位置:实体类主键字段
@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 |
指定主键类型(在不写type类型的时候默认为雪花算法) |
需要提前设置数据库自增
如果需要自己写主键,可以自己配置全局的主键生成策略
值 |
描述 |
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 长整型类型(pleaseuseASSIGN_ID) |
UUID |
32 位 UUID 字符串(please use ASSIGN_UUID ) |
ID_WORKER_STR |
分布式全局唯一 ID 字符串类型(please use ASSIGN_ID ) |
描述:字段注解(非主键)
@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 |
是否为数据库表字段 |
condition |
String |
否 |
"" |
字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s} , 参考 |
描述:乐观锁注解,标记@version在字段上
描述:普通枚举类注解(标注在枚举字段上)
描述:表字段逻辑处理注解(逻辑删除)
属性 |
类型 |
必须指定 |
默认值 |
描述 |
value |
String |
否 |
"" |
逻辑未删除值 |
delval |
String |
否 |
"" |
逻辑删除值 |
描述:内置Sql默认指定排序,优先级低于wrapper条件查询
属性 |
类型 |
必须指定 |
默认值 |
描述 |
asc |
boolean |
否 |
true |
是否倒序查询 |
sort |
short |
否 |
Short.MAX_VALUE |
数字越小越靠前 |
写在Dao类上,将类注入Spring容器中
@MapperScan(value = {"com.aqiuo.dao"})
写在启动类上,将需要扫描的包写在value中
CRUD接口
说明: 通用 Service CRUD 封装IService (opens new window)
// 插入一条记录(选择字段,策略插入) boolean save(T entity); // 插入(批量) boolean saveBatch(Collection
参数说明
类型 |
参数名 |
描述 |
T |
entity |
实体对象 |
Collection |
entityList |
实体对象集合 |
int |
batchSize |
插入批次数量 |
// TableId 注解存在更新记录,否插入一条记录,
//如果有这个记录,则修改记录,如果没有就插入记录
//如果没有id就是插入,有就是修改
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection entityList, int batchSize);
参数说明
类型 |
参数名 |
描述 |
T |
entity |
实体对象 |
Wrapper |
updateWrapper |
实体对象封装操作类 UpdateWrapper |
Collection |
entityList |
实体对象集合 |
int |
batchSize |
插入批次数量 |
// 根据 queryWrapper 设置的条件,删除记录
boolean remove(Wrapper queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件,删除记录(map存的条件)
boolean removeByMap(Map columnMap);
// 删除(根据ID 批量删除)
boolean removeByIds(Collection extends Serializable> idList);
参数说明
类型 |
参数名 |
描述 |
Wrapper |
queryWrapper |
实体包装类 QueryWrapper |
Serializable |
id |
主键 ID |
Map |
columnMap |
表字段 map 对象 |
Collection |
idList |
主键 ID 列表 |
(注意updateById才能根据id修改内容,其他的要用wrapper)
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper updateWrapper);
// 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection entityList, int batchSize);
参数说明
类型 |
参数名 |
描述 |
Wrapper |
updateWrapper |
实体对象封装操作类 UpdateWrapper |
T |
entity |
实体对象 |
Collection |
entityList |
实体对象集合 |
int |
batchSize |
更新批次数量 |
Get
// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper queryWrapper);
// 根据 Wrapper,查询一条记录
T getOne(Wrapper queryWrapper, boolean throwEx);
// 根据 Wrapper,查询一条记录
Map getMap(Wrapper queryWrapper);
// 根据 Wrapper,查询一条记录
V getObj(Wrapper queryWrapper, Function super Object, V> mapper);
参数说明
类型 |
参数名 |
描述 |
Serializable |
id |
主键 ID |
Wrapper |
queryWrapper |
实体对象封装操作类 QueryWrapper |
boolean |
throwEx |
有多个 result 是否抛出异常 |
T |
entity |
实体对象 |
Function |
mapper |
转换函数 |
List
// 查询所有
List list();
// 查询列表
List list(Wrapper queryWrapper);
// 查询(根据ID 批量查询)
Collection listByIds(Collection extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection listByMap(Map columnMap);
// 查询所有列表
List
参数说明
类型 |
参数名 |
描述 |
Wrapper |
queryWrapper |
实体对象封装操作类 QueryWrapper |
Collection |
idList |
主键 ID 列表 |
Map |
columnMap |
表字段 map 对象 |
Function |
mapper |
转换函数 |
Page
// 无条件分页查询
IPage page(IPage page);
// 条件分页查询
IPage page(IPage page, Wrapper queryWrapper);
// 无条件分页查询
IPage> pageMaps(IPage page);
// 条件分页查询
IPage> pageMaps(IPage page, Wrapper queryWrapper);
参数说明
类型 |
参数名 |
描述 |
IPage |
page |
翻页对象 |
Wrapper |
queryWrapper |
实体对象封装操作类 QueryWrapper |
Count
// 查询总记录数 int count(); // 根据 Wrapper 条件,查询总记录数 int count(Wrapper
参数说明
类型 |
参数名 |
描述 |
Wrapper |
queryWrapper |
实体对象封装操作类 QueryWrapper |
(存在有一个静态方法引用非静态方法的问题)
// 链式查询 普通
QueryChainWrapper query();
// 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper lambdaQuery();
// 示例:
query().eq("column", value).one();
lambdaQuery().eq(Entity::getId, value).list();
// 链式更改 普通
UpdateChainWrapper update();
// 链式更改 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper lambdaUpdate();
// 示例:
update().eq("column", value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);
说明: 通用 CRUD 封装BaseMapper接口,为Mybatis-Plus启动时自动解析实体表关系映射转换为Mybatis内部对象注入容器 泛型 T 为任意实体类对象 参数 Serializable为任意类型的主键,Mybatis-Plus不推荐使用复合主键约定每一张表都有自己唯一的id主键 对象Wrapper 条件构造器
// 插入一条记录 int insert(T entity);
参数说明
类型 |
参数名 |
描述 |
T |
entity |
实体对象 |
// 根据 entity 条件,删除记录 int delete(@Param(Constants.WRAPPER) Wrapper
参数说明
类型 |
参数名 |
描述 |
Wrapper |
wrapper |
实体对象封装操作类(可以为 null) |
Collection |
idList |
主键 ID 列表(不能为 null 以及 empty) |
Serializable |
id |
主键 ID |
Map |
columnMap |
表字段 map 对象 |
// 根据 whereWrapper 条件,更新记录 int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper
使用提示:
在调用updateById方法前,需要在T entity(对应的实体类)中的主键属性上加上@TableId注解。
参数说明
类型 |
参数名 |
描述 |
T |
entity |
实体对象 (set 条件值,可为 null) |
Wrapper |
updateWrapper |
实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) |
// 根据 ID 查询 T selectById(Serializable id); // 根据 entity 条件,查询一条记录 T selectOne(@Param(Constants.WRAPPER) Wrapper
参数说明
类型 |
参数名 |
描述 |
Serializable |
id |
主键 ID |
Wrapper |
queryWrapper |
实体对象封装操作类(可以为 null) |
Collection |
idList |
主键 ID 列表(不能为 null 以及 empty) |
Map |
columnMap |
表字段 map 对象 |
IPage |
page |
分页查询条件(可以为 RowBounds.DEFAULT) |
说明:
警告
AbstractWrapper
说明:
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
allEq
allEq(Map
全部eq(或个别isNull)
个别参数说明:
例一:allEq({id:1,name:"老王",age:null}) --->id = 1 and name = '老王' and age is null
例二:allEq({id:1,name:"老王",age:null}, false)-->id = 1 and name = '老王'
eq
eq(R column, Object val) eq(boolean condition, R column, Object val)
等于 =
例: eq("name", "老王") - --> name = '老王'
ne
ne(R column, Object val) ne(boolean condition, R column, Object val)
不等于 <>
例: ne("name", "老王") ---> name <> '老王'
gt
gt(R column, Object val) gt(boolean condition, R column, Object val)
ge
ge(R column, Object val) ge(boolean condition, R column, Object val)
lt
lt(R column, Object val) lt(boolean condition, R column, Object val)
#le
le(R column, Object val) le(boolean condition, R column, Object val)
#between
between(R column, Object val1, Object val2) between(boolean condition, R column, Object val1, Object val2)
#notBetween
notBetween(R column, Object val1, Object val2) notBetween(boolean condition, R column, Object val1, Object val2)
#like
like(R column, Object val) like(boolean condition, R column, Object val)
#notLike
notLike(R column, Object val) notLike(boolean condition, R column, Object val)
#likeLeft
likeLeft(R column, Object val) likeLeft(boolean condition, R column, Object val)
likeRight
likeRight(R column, Object val) likeRight(boolean condition, R column, Object val)
notLikeLeft
notLikeLeft(R column, Object val) notLikeLeft(boolean condition, R column, Object val)
notLikeRight
notLikeRight(R column, Object val) notLikeRight(boolean condition, R column, Object val)
#isNull
isNull(R column) isNull(boolean condition, R column)
isNotNull
isNotNull(R column) isNotNull(boolean condition, R column)
in
in(R column, Collection> value) in(boolean condition, R column, Collection> value)
in(R column, Object... values) in(boolean condition, R column, Object... values)
notIn
notIn(R column, Collection> value) notIn(boolean condition, R column, Collection> value)
notIn(R column, Object... values) notIn(boolean condition, R column, Object... values)
inSql
inSql(R column, String inValue) inSql(boolean condition, R column, String inValue)
notInSql
notInSql(R column, String inValue) notInSql(boolean condition, R column, String inValue)
groupBy
groupBy(R... columns) groupBy(boolean condition, R... columns)
orderByAsc
orderByAsc(R... columns) orderByAsc(boolean condition, R... columns)
orderByDesc
orderByDesc(R... columns) orderByDesc(boolean condition, R... columns)
orderBy
orderBy(boolean condition, boolean isAsc, R... columns)
having
having(String sqlHaving, Object... params) having(boolean condition, String sqlHaving, Object... params)
func
func(Consumer
or
or() or(boolean condition)
注意事项:
主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)
or(Consumer consumer) or(boolean condition, Consumer consumer)
and
and(Consumer consumer) and(boolean condition, Consumer consumer)
nested
nested(Consumer consumer) nested(boolean condition, Consumer consumer)
apply
apply(String applySql, Object... params) apply(boolean condition, String applySql, Object... params)
该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!
last
last(String lastSql) last(boolean condition, String lastSql)
注意事项:
只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
exists
exists(String existsSql) exists(boolean condition, String existsSql)
notExists
notExists(String notExistsSql) notExists(boolean condition, String notExistsSql)
QueryWrapper
说明:
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取
select
select(String... sqlSelect) select(Predicate
说明:
以上方法分为两类.
第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper内的entity属性有值! 这两类方法重复调用以最后一次为准
UpdateWrapper
说明:
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!
set
set(String column, Object val) set(boolean condition, String column, Object val)
setSql
setSql(String sql)
lambda
(这个lambda可以用方法引用,其他不行,支持链式编程)
在QueryWrapper是LambdaQueryWrapper
在UpdateWrapper是LambdaUpdateWrapper
乐观OptimisticLockerInnerInterceptor
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前 version更新时,带上这个 version执行更新时, set version = newVersion where version = oldVersion如果 version 不对,就更新失败
spring boot 注解方式:
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }
2.在实体类的字段上加上@Version注解
@Version private Integer version;
示例
// 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; } }
代码生成器
引入依赖
快速生成
FastAutoGenerator.create("url", "username", "password") .globalConfig(builder -> { builder.author("baomidou") // 设置作者 .enableSwagger() // 开启 swagger 模式 .fileOverride() // 覆盖已生成文件 .outputDir("D://"); // 指定输出目录 }) .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> { int typeCode = metaInfo.getJdbcType().TYPE_CODE; if (typeCode == Types.SMALLINT) { // 自定义类型转换 return DbColumnType.INTEGER; } return typeRegistry.getColumnType(metaInfo); })) .packageConfig(builder -> { builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名 .moduleName("system") // 设置父包模块名 .pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径 }) .strategyConfig(builder -> { builder.addInclude("t_simple") // 设置需要生成的表名 .addTablePrefix("t_", "c_"); // 设置过滤表前缀 }) .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板 .execute();