mybatis-plus使用笔记、wrappers用法、idGenerator(id生成器等)
2026/6/11 9:23:21 网站建设 项目流程

文章目录

      • 打印日志
        • createOrUpdate
    • wrappers
      • 什么是wrappers、它的作用
      • wrappers query
      • wrappers query的另外一种方式
        • wrappers查询某字段长度等于20
      • wrappers update
        • wrapper update时设置某个字段为空
          • wrapper update-错误的写法示例
          • wrapper update-正确的写法示例1(推荐)
          • wrapper update-正确的写法示例2(还得手动拼对象)
        • update时更新策略的坑
          • wrapper update总结
        • wrappers or 条件
        • wrappers 批量or条件
        • or的两个条件分别判断
      • 根据入参是否有值决定是否拼接语句
        • wrappers可以实现根据入参不指定条件的查询吗?
        • resultMap中的extends什么意思
    • idGenerator(id生成器)
      • id生成器-自带
      • id生成器-自定义
        • 方案一
        • 方案二
      • 全局配置
        • selectOne和selectList
    • 其他
        • mybatisplus和mybatis的区别
      • 文档及文章

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

打印日志

开启打印:

mybatis-plus:configuration:### 开启打印sql配置log-impl:org.apache.ibatis.logging.stdout.StdOutImpl### 开启驼峰配置map-underscore-to-camel-case:true

关闭打印:

mybatis-plus:configuration:#关闭sql日志log-impl:org.apache.ibatis.logging.nologging.NoLoggingImpl
createOrUpdate

createOrUpdate是根据入参中是否有id来判断的
如果无id,认为是新增
如果有id,认为是更新

这里有个注意点,它并不具有判断id在数据库中是否存在的功能

所以如果你的需求是:
如果id在数据库中不存在,则新增。如果在数据库中已存在,则更新。
那么用createOrUpdate方法是不行的。

wrappers

什么是wrappers、它的作用

wrappers是mybatis-plus的一个通用接口,用于结构化查询条件。

个人感觉,它最大的作用就是将sql语句通过java形式封装起来了,使得可以通过java形式实现各种维度的sql组装。

wrappers query

示例:

userMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getId,user.getId()).eq(User::getUserName,user.getUserName())

wrappers query的另外一种方式

LambdaQueryWrapper<User>userQueryWrapper=Wrappers.lambdaQuery(User.class);if(!StringUtils.isEmpty(request.getName())){userQueryWrapper.eq(User::getName,request.getName());}userQueryWrapper.eq(User::getUserAccount,request.getUid());userQueryWrapper.eq(User::getIsDel,newBigDecimal(0));Useruser=userMapper.selectOne(userQueryWrapper);
wrappers查询某字段长度等于20

代码:

LambdaQueryWrapper<User>userQueryWrapper=Wrappers.lambdaQuery(User.class);userQueryWrapper.apply("LENGTH(user_account) = {0}",20)

注:length里面的字段是数据库字段名,不是实体类字段名

wrappers update

例如:

userMapper.update(user,Wrappers.lambdaUpdate(User.class).eq(user::getId,user.getId())
wrapper update时设置某个字段为空

注:这里非常容易踩坑
自己也踩过,也见过其他开发踩过,下面做了下整理。

wrapper update-错误的写法示例

错误的代码:

intupdateI=userMapper.update(user,Wrappers.lambdaUpdate(User.class).eq(User::getId,user.getId()).set(User::getIsDel,user.getIsDel());

这样会报错ORA-00957: duplicate column name。
提示很明显,属性重复了。
查看下sql发现:

updatet_usersetis_del='1',is_del='1'whereid='1';-- 果然是字段重复了
wrapper update-正确的写法示例1(推荐)
Useruser=newUser();user.setId();user.setIsDel();intupdateI=userMapper.update(null,Wrappers.lambdaUpdate(User.class).eq(User::getId,user.getId()).set(User::getIsDel,user.getIsDel()).eq(User::getUserAccount,user.getUserAccount()));
wrapper update-正确的写法示例2(还得手动拼对象)
Useruser=newUser();user.setId();user.setIsDel();intupdateI=userMapper.update(user,Wrappers.lambdaUpdate(User.class).eq(User::getId,user.getId()));
update时更新策略的坑

场景:
根据id更新状态,按道理只应该更新状态,但是发现把记账时间字段设置为null了。
看了下sql发现果然,set accounting_time =null。

@ApiModelProperty(value="记账时间")@TableField(updateStrategy=FieldStrategy.IGNORED)privateDateaccountingTime;

百度了下果然是这样:
FieldStrategy.IGNORED 是MyBatis-Plus 框架中用于控制字段在数据库操作(如插入或更新)时是否参与值判断的一种策略。它指示框架在生成 SQL 语句时,忽略该字段的空值(null)检查,无论字段值是否为 null,都会将其拼接到 SQL 中。‌
所以问题就在这里,注释掉即可。

wrapper update总结

总结:
1、参数1传入实际对象,后续千万不要显示的set了,会报错字段重复,只eq id即可,会更新所有的非空字段。(适合要改的字段较多的情况,不用一个一个改了)
2、参数1传null,需要set哪个值就set哪个值。(适合要改的字段不多的场景)

wrappers or 条件

例如:(create_date >‘2025-12-09’) or (update_date <‘2025-12-09’)

LambdaQueryWrapper<User>bjmQuery=newLambdaQueryWrapper<>();bjmQuery.and(wrapper->wrapper.ge(User::getCreateDate,startDate).le(User::getCreateDate,endDate)).or(wrapper->wrapper.ge(User::getUpdateTime,startDate).le(User::getUpdateTime,endDate));

实测可行。

wrappers 批量or条件

如果是一个字段,用in就ok了,简单方便。
有些场景需要用批量or。
1、条数超过1000不能用in。
2、每条数据多条件不能用in,只能拼好后再or。

这个是多条件or,代码:

LambdaQueryWrapper<User>queryWrapper=Wrappers.lambdaQuery(User.class);for(IamUseritem:IamUsers){queryWrapper.or(a->a.eq(!StringUtils.isEmpty(item.getId()),User::getId,item.getId()).eq(User::getUserName,item.getUserName()));}

单条件的更简单,代码:

LambdaQueryWrapper<User>queryWrapper=Wrappers.lambdaQuery(User.class);for(IamUseritem:IamUsers){queryWrapper.or(a->a.eq(!StringUtils.isEmpty(item.getId()),User::getId,item.getId()));}
or的两个条件分别判断

场景:
筛选 创建时间>10天前 或 更新时间>10天前 的数据。

代码:

if(!ObjectUtils.isEmpty(request.getCreateTime())||!ObjectUtils.isEmpty(request.getUpdateTime())){queryWrapper.or(wrapper->{if(!ObjectUtils.isEmpty(request.getCreateTime())){wrapper.ge(User::getCreateTime,request.getCreateTime());}if(!ObjectUtils.isEmpty(request.getUpdateTime())){if(!ObjectUtils.isEmpty(request.getCreateTime())){wrapper.or();// 只有前面有条件时才加 or}wrapper.ge(User::getUpdateTime,request.getUpdateTime());}});}

根据入参是否有值决定是否拼接语句

当然可以做到。
例如:用户账号不为空的情况下才加这个条件。

Useruser=newUser();user.setId();user.setIsDel();intupdateI=userMapper.update(null,Wrappers.lambdaUpdate(User.class).eq(User::getId,user.getId()).set(User::getIsDel,user.getIsDel()).eq(!StringUtils.isEmpty(user.getUserAccount),User::getUserAccount,user.getUserAccount()));
wrappers可以实现根据入参不指定条件的查询吗?

是这样,selectByCondition()只用传入一个入参,就可以实现多条件查询

wrappers可以吗?
个人感觉应该够呛,因为selectByCondition()是有xml语句定义的,所以只用一个入参即可
但是wrappers并没有语句定义,所以只能在wrappers里面把条件写上。

注: 这里持保留态度吧,也许有但个人不知道也是有可能的。

resultMap中的extends什么意思

如:

<resultMapid="BaseResultMap"type="com.User"extends="com.UserMapper.mybatis-plus_User">

extends并不陌生,在mybatis中就已经有了,表示继承。

个人感觉,这里的mybatis-plus_应该是一种定式写法,后面跟实际的类名,这样就不用手动写字段了。

idGenerator(id生成器)

mybatis自带id生成器,也支持自定义id生成器。

id生成器-自带

自带的id生成器是DefaultIdentifierGenerator,在IdentifierGeneratorAutoConfiguration配置类中用到。
代码:

publicclassIdentifierGeneratorAutoConfiguration{publicIdentifierGeneratorAutoConfiguration(){}@Bean@ConditionalOnMissingBeanpublicIdentifierGeneratoridentifierGenerator(InetUtilsinetUtils){returnnewDefaultIdentifierGenerator(inetUtils.findFirstNonLoopbackAddress());}}

这个是如何实现分布式的呢?
实际上并未用到调度中心,但是通过一系列算法,应该能一定程度的实现分布式。

核心是Sequence类中的这一段。

NetworkInterfacenetwork=NetworkInterface.getByInetAddress(this.inetAddress);if(null==network){id=1L;}else{byte[]mac=network.getHardwareAddress();if(null!=mac){id=(255L&(long)mac[mac.length-2]|65280L&(long)mac[mac.length-1]<<8)>>6;id%=maxDatacenterId+1L;}}

这段代码没看懂,到底是如何实现分布式(或伪分布式的呢)? TODO
简言之,就是取mac地址的后两位,如:ff:ff进行运算。
这块是位运算的知识。

这种可能重复吗?
可能重复,但是概率不高,而且在初始化的时候就能判断出来、
再不借助第三方调度中心的情况下,这算是比较好的方案了,至少比直接0,0强。

id生成器-自定义

boot和spring配置方式不一样。

这里以boot为例。

方案一

自定义类实现IdentifierGenerator接口,注入该类即可。

@ComponentpublicclassCustomIdGeneratorimplementsIdentifierGenerator{@OverridepublicLongnextId(Objectentity){// 使用实体类名作为业务键,或者提取参数生成业务键StringbizKey=entity.getClass().getName();// 根据业务键调用分布式ID生成服务longid=...;// 调用分布式ID生成逻辑// 返回生成的ID值returnid;}}
方案二

注入IdentifierGenerator,自定义方法。

@BeanpublicIdentifierGeneratoridGenerator(){returnnewCustomIdGenerator();}

全局配置

全局配置类是GlobalConfig,这个类有时间建议好好看看。

selectOne和selectList

之前一直用selectList,因为感觉有时selectOne会报错,使代码变的不可控。
实际弄清楚之后就不会有这个担忧了。

方法描述
selectList不会报错,如果无数据返回一个空list
selectOne无记录返回null,有一条返回对象,多条直接报错

其他

mybatisplus和mybatis的区别
维度mybatismybatisplus
核心区别mybatis需要手动写sql,灵活性极高plus旨在减少开发量,极大提高开发效率,且兼容mybatis
BaseMapper,IService,ServiceImplBaseMapper实现了大多数通用方法,实际mapper通过实现BaseMapper即可复用方法。ServiceImpl实现了IService接口,实际service通过继承ServiceImpl并实现IService,可以复用很多方法。
sql拼接通过xml标签编辑动态sql通过wrappers条件构造
分页使用pageHelper插件内置分页插件,可直接使用Page对象和selectPage方法实现分页查询
其他增强功能代码生成,主键策略(uuid、雪花算法等),id生成器等
逻辑删除、自动填充‌:通过注解即可实现软删除和自动填充创建时间等字段

注:如果主要用wrapper语法,在用mapper.xml,那么要解析的xml数量也会少很多,项目启动会快一些。

文档及文章

mybatis-plus官网中文文档
id生成器官网文档

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询