文章目录
- 打印日志
- 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.NoLoggingImplcreateOrUpdate
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的区别
| 维度 | mybatis | mybatisplus |
|---|---|---|
| 核心区别 | mybatis需要手动写sql,灵活性极高 | plus旨在减少开发量,极大提高开发效率,且兼容mybatis |
| BaseMapper,IService,ServiceImpl | 略 | BaseMapper实现了大多数通用方法,实际mapper通过实现BaseMapper即可复用方法。ServiceImpl实现了IService接口,实际service通过继承ServiceImpl并实现IService,可以复用很多方法。 |
| sql拼接 | 通过xml标签编辑动态sql | 通过wrappers条件构造 |
| 分页 | 使用pageHelper插件 | 内置分页插件,可直接使用Page对象和selectPage方法实现分页查询 |
| 其他增强功能 | 略 | 代码生成,主键策略(uuid、雪花算法等),id生成器等 逻辑删除、自动填充:通过注解即可实现软删除和自动填充创建时间等字段 |
注:如果主要用wrapper语法,在用mapper.xml,那么要解析的xml数量也会少很多,项目启动会快一些。
文档及文章
mybatis-plus官网中文文档
id生成器官网文档