Kotlin 版 MyBatis-Plus 查询优雅解决方案
从 Java 切换到 Kotlin 之后,总觉得 MyBatis-Plus 的查询写法还是啰嗦:要么用 KtQueryWrapper 写一长串 User::getName,要么在动态搜索表单里写一堆 if-else 判空,视觉噪音还是大
于是我写了一个扩展库 ------ mybatis-plus-kt ,利用 KProperty1 属性引用,让你用类型安全、接近自然语言的风格写查询,彻底告别字符串和重复判空。
kotlin
// 就这样,读起来像说话一样自然
User::name eq "btown"
User::age ge 18
User::role `in` listOf("admin", "dev")
✨ 核心亮点
- 类型安全 :
User::name是编译期检查的,再也不会把字段名写错 - 类自然语言 :
User::age between (18 to 60)读作"年龄在 18 到 60 之间" - 零配置:Spring Boot 自动配置,引入依赖即用
- 安全模式 :
ktWhereSafe { }里 null / 空集合自动跳过,零 if-else - 无侵入 :复用
QueryWrapper,直接.selectList()就能执行
🚀 5 分钟快速上手
1. 引入依赖
Gradle
kotlin
implementation("io.github.btown021:mybatis-plus-kt-spring-boot-starter:1.0.1")
Maven
xml
<dependency>
<groupId>io.github.btown021</groupId>
<artifactId>mybatis-plus-kt-spring-boot-starter</artifactId>
<version>1.0.1</version>
</dependency>
2. 定义实体与 Service
kotlin
@TableName("sys_user")
data class User(
var name: String = "",
var age: Int = 0,
var status: Int = 0,
var role: String = ""
) : BaseAttr()
interface IUserService : IService<User>
class UserServiceImpl : ServiceImpl<UserMapper, User>(), IUserService
3. 查询就是这么简单
kotlin
@Service
class UserBizService(private val userService: IUserService) {
fun search(name: String?, status: Int?, roleList: List<String>?): List<User> {
return userService.createQueryWrapper<User>()
// 安全模式:自动跳过 null,空集合生成永假条件,between 单端 null 自动降级
.ktWhereSafe {
User::name like name // null 自动跳过
User::status eq status // null 自动跳过
User::role in null // null 自动跳过
User::role in enmptyList() // 转换为生成永假条件 (无匹配)
User::age between (null to 60) // 降级为 age <= 60
User::age between (18 to null) // 降级为 age >= 18
User::age between (null to null) // 自动跳过
}
// 普通模式 原生MP行为 不进行null的跳过 降级等
.ktWhere {
User::name like name
User::status eq status
}
.ktOrder { User::id.orderByDesc }
.selectList()
}
}
空集合的智能处理:in 操作遇到空集合时,不能简单跳过(否则会查出全部数据),安全模式会自动生成 1<>1,保证结果为空,更符合业务直觉。notIn 空集合则直接跳过。
不用写一行 if (name != null) { ... },安全模式帮你把所有空判断都处理好了。
🤯 减少 if-else 样板代码 (安全模式)
原生写法:
kotlin
val wrapper = QueryWrapper<User>()
if (name != null) wrapper.lambda().like(User::getName, name)
if (status != null) wrapper.lambda().eq(User::getStatus, status)
if (ageMin != null) wrapper.lambda().ge(User::getAge, ageMin)
if (ageMax != null) wrapper.lambda().le(User::getAge, ageMax)
if (!roleList.isNullOrEmpty()) wrapper.lambda().`in`(User::getRole, roleList)
mybatis-plus-kt 写法:
kotlin
userService.createQueryWrapper()
.ktWhereSafe {
User::name like name
User::status eq status
User::age between (ageMin to ageMax)
User::role in roleList
}
.selectList()
代码量直接减半,可读性肉眼可见地提升。
🧩 强大的逻辑组合
支持 and / or / anyOf 等逻辑,复杂条件也写得优雅:
kotlin
ktWhere {
User::status eq 1
and {
User::role eq "admin"
or {
User::level ge 5
User::vip eq true
}
}
}
// 生成 SQL: WHERE status = 1 AND (role = 'admin' OR level >= 5 OR vip = TRUE)
// anyOf:对同一字段的多候选值 OR 匹配
anyOf(listOf("a", "b", "c")) {
User::name like it
}
// SQL: WHERE (name LIKE '%a%' OR name LIKE '%b%' OR name LIKE '%c%')
排序、指定返回列也都支持,且保持同样的自然语法:
kotlin
.ktOrder { User::status.orderByAsc; User::id.orderByDesc }
.ktSelect(User::id, User::name)
⚙️ 自定义列名映射
默认从 @TableField 读取映射,你也可以实现 ColumnNameProvider 来覆盖:
kotlin
override fun resolveFieldColumnMap(): Map<String, String> = mapOf(
"name" to "user_name"
)
甚至支持运行时动态列名(分表、多租户场景),只需将 isDynamic() 设为 true 即可。
📦 环境
- Kotlin 2.2+ / JDK 17+
- MyBatis-Plus 3.5.3+
- Spring Boot 3.0+
🎯 总结
mybatis-plus-kt 在不改变 MyBatis-Plus 原有使用习惯的前提下,用 Kotlin 特性让查询代码更安全、更简洁,写起来就像说话一样自然。特别适合动态查询多、表单搜索频繁的后台系统。如果你也在用 Kotlin 写 Spring Boot,强烈推荐试一下。
👉 GitHub: github.com/btown021/my... 👉 Gitee: gitee.com/btown/mybat...
(顺手点个 Star 支持一下呗 ⭐)