驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
MyBatis Plus的一些"高级"用法
/  

MyBatis Plus的一些"高级"用法

MP的一些高级用法.md

开篇

好久没更新文章了,是在有些堕落了,最近项目有些忙,加班有些多,给自己提升和思考总结的时间少了些,昨晚刚好有空,整理了下之前的笔记,发现了项目使用中记录的一些MybatisPlus的总结,这里重新整理下。

建议

建议1:使用MybatisPlus生成实体类的时候,设置entityColumnConstant=true将字段常量一起生成。

建议2:新建一个自己的业务实体类XxBaseService继承MyBatisPlus自带的com.baomidou.mybatisplus.extension.service.impl.ServiceImpl,好处是在XxBaseService中你可以自己扩展一些基础方法

建议3:通过ServiceImpl中的QueryChainWrapper<T> query()UpdateChainWrapper<T> update()进行数据的CRUD。该建议同建议1进行搭配。

之所以不使用LambdaQueryChainWrapper<T> lambdaQuery()

  • 一是因为生成的字段常量的方式性能更好,少了一层反射。
  • 二是字符串形式的select支持自定义的列,比如select(SignHospital.FROM_HOS_NAME,"date_format(sign_date,'%Y-%m-%d') as sign_date2")
  • 三是因为其不支持Kotlin,我们服务端50%的代码都是Kotlin了。

之所以不适用传统的方式,那是因为传统的方式不支持链式写法,不够优美。

建议4:开发阶段通过p6spy打印SQL,方便排除问题,可以参考官网:执行 SQL 分析打印 | MyBatis-Plus

建议5:通过select("列1","列2")来筛选需要的内容,特别是字段列很多,单你仅仅需要1-2个的时候。

写法

or

查询的时候,带有or的时候,可以先通过and引导,然后在条件中写or

案例1:

val resPage = query()
            .gt(pageReqHos.beginSignDate != null, SignHospital.SIGN_DATE, pageReqHos.beginSignDate)
            .lt(pageReqHos.endSignDate != null, SignHospital.SIGN_DATE, pageReqHos.endSignDate)
            .and(!pageReqHos.hosName.isNullOrBlank()) {
                it.like(SignHospital.FROM_HOS_NAME, pageReqHos.hosName)
                    .or()
                    .like(SignHospital.TO_HOS_NAME, pageReqHos.hosName)
            }.page(page)

案例2:

List<UserCore> list = super.lambdaQuery()
                .select(UserCore::getId, UserCore::getHospitalId, UserCore::getHospitalName)
                .eq(UserCore::getAsDelete, 0)
                .and(p -> p.eq(UserCore::getHospitalName, hosName).or().eq(UserCore::getHospitalId, hosId))
                .last(" limit 1000").list();

first和last

first用的很少,不过last用的很多,他会在where条件后拼接内容,比如业务中进行查询的时候,很多时候都建议带上一个超过正常预期范围2倍的limit 2n,这样可以有效的避免查询出非常多内容后出现的OOM

比如查询一个班级的学生,业务已经很明确的知晓了一个班级最大60名学生,此时你作为防御机制,可以在查询的时候,跟上limit 120,可以避免你手抽的时候没有写上where 班级id=1导致的一些类似问题。

query().eq(MxUser.GROUP_ID,'123').last("limit 100")

对应的SQL只能查出最大的100名用户,这里请注意你的阈值,不要同业务冲突。

SELECT * FROM mx_user WHERE group_id='123' LIMIT 100

可以利用apply或者inSql来完成部分内容。

同理你的Update其实也可以带上LIMIT的,这也是一个防御策略。

apply

一个很典型的需求就是在查询的时候,需要引入SQL的函数,比如在一张签约表中,查询2021年签约的医院。

query().apply("date_format(sign_date,'%Y')={0}","2022").list()

对应出的sql

SELECT * FROM sign_hos WHERE date_format(sign_date,'%Y')='2022&#39;

inSql

比如学生表中,需要查询的是在某些班级的学生,此时加入某些班级以及确定的情况下,可以使用in,但是假如这些班级不确定,而是通过另外的sql语句进行查询出来的,此时推荐使用inSql

query().inSql("c_id","SELECT id FROM spec_cls WHERE type=1").list()

对应的sql

SELECT * FROM mx_user WHERE c_id IN
(
    SELECT id FROM spec_cls WHERE type=1
)

nested

我对这个用的少,大概的意思是就是括号包装下

结语

其实MybatisPlus带有更多的高阶用法,不过我个人建议不必太过执着的一定要通过API的形式完成复杂的SQL,直接在XML中书写可能也是一个非常的方法,请根据自己团队对MP的熟悉程度做出权衡。

骐骥一跃,不能十步。驽马十驾,功在不舍。