0
# Kotlin Extensions
1
2
MyBatis-Plus Extension provides comprehensive Kotlin support through specialized wrapper classes that leverage Kotlin's language features for type-safe database operations. These extensions offer property-based query building using Kotlin's reflection capabilities and provide a more idiomatic Kotlin development experience.
3
4
## Core Components
5
6
### KtQueryWrapper
7
8
Type-safe query wrapper for Kotlin with property-based field references.
9
10
```kotlin { .api }
11
class KtQueryWrapper<T>(entity: T? = null) : AbstractKtWrapper<T, KtQueryWrapper<T>>() {
12
constructor(entityClass: Class<T>) : this(null)
13
14
// Select operations
15
fun select(vararg columns: KProperty<*>): KtQueryWrapper<T>
16
fun getSqlSelect(): String
17
18
// Factory methods
19
fun instance(): KtQueryWrapper<T>
20
fun clear(): KtQueryWrapper<T>
21
22
// Inherited condition methods from AbstractKtWrapper
23
// eq, ne, gt, ge, lt, le, like, notLike, in, notIn, etc.
24
}
25
```
26
27
### KtUpdateWrapper
28
29
Type-safe update wrapper for Kotlin operations.
30
31
```kotlin { .api }
32
class KtUpdateWrapper<T>(entity: T? = null) : AbstractKtWrapper<T, KtUpdateWrapper<T>>() {
33
constructor(entityClass: Class<T>) : this(null)
34
35
// Set operations using Kotlin properties
36
fun set(column: KProperty<*>, value: Any?): KtUpdateWrapper<T>
37
fun setSql(sql: String): KtUpdateWrapper<T>
38
39
// Factory methods
40
fun instance(): KtUpdateWrapper<T>
41
fun clear(): KtUpdateWrapper<T>
42
}
43
```
44
45
### KtQueryChainWrapper
46
47
Kotlin chain wrapper for fluent query operations.
48
49
```kotlin { .api }
50
class KtQueryChainWrapper<T> : AbstractChainWrapper<T, KProperty<*>, KtQueryChainWrapper<T>, KtQueryWrapper<T>>(),
51
ChainQuery<T> {
52
53
constructor(baseMapper: BaseMapper<T>)
54
constructor(entityClass: Class<T>)
55
56
// Terminal operations
57
override fun list(): List<T>
58
override fun one(): T
59
override fun oneOpt(): Optional<T>
60
override fun count(): Long
61
override fun exists(): Boolean
62
override fun <E : IPage<T>> page(page: E): E
63
64
// Select operations
65
fun select(vararg columns: KProperty<*>): KtQueryChainWrapper<T>
66
}
67
```
68
69
### KtUpdateChainWrapper
70
71
Kotlin chain wrapper for fluent update operations.
72
73
```kotlin { .api }
74
class KtUpdateChainWrapper<T> : AbstractChainWrapper<T, KProperty<*>, KtUpdateChainWrapper<T>, KtUpdateWrapper<T>>(),
75
ChainUpdate<T> {
76
77
constructor(baseMapper: BaseMapper<T>)
78
constructor(entityClass: Class<T>)
79
80
// Terminal operations
81
override fun update(): Boolean
82
override fun update(entity: T): Boolean
83
override fun remove(): Boolean
84
85
// Set operations
86
fun set(column: KProperty<*>, value: Any?): KtUpdateChainWrapper<T>
87
fun setSql(sql: String): KtUpdateChainWrapper<T>
88
}
89
```
90
91
### AbstractKtWrapper
92
93
Base class for Kotlin wrappers providing type-safe condition methods.
94
95
```kotlin { .api }
96
abstract class AbstractKtWrapper<T, Children : AbstractKtWrapper<T, Children>> {
97
98
// Comparison conditions
99
fun eq(column: KProperty<*>, value: Any?): Children
100
fun ne(column: KProperty<*>, value: Any?): Children
101
fun gt(column: KProperty<*>, value: Any?): Children
102
fun ge(column: KProperty<*>, value: Any?): Children
103
fun lt(column: KProperty<*>, value: Any?): Children
104
fun le(column: KProperty<*>, value: Any?): Children
105
106
// String conditions
107
fun like(column: KProperty<*>, value: Any?): Children
108
fun notLike(column: KProperty<*>, value: Any?): Children
109
fun likeLeft(column: KProperty<*>, value: Any?): Children
110
fun likeRight(column: KProperty<*>, value: Any?): Children
111
112
// Null conditions
113
fun isNull(column: KProperty<*>): Children
114
fun isNotNull(column: KProperty<*>): Children
115
116
// Collection conditions
117
fun `in`(column: KProperty<*>, values: Collection<*>): Children
118
fun notIn(column: KProperty<*>, values: Collection<*>): Children
119
fun `in`(column: KProperty<*>, vararg values: Any?): Children
120
fun notIn(column: KProperty<*>, vararg values: Any?): Children
121
122
// Range conditions
123
fun between(column: KProperty<*>, val1: Any?, val2: Any?): Children
124
fun notBetween(column: KProperty<*>, val1: Any?, val2: Any?): Children
125
126
// Logical operators
127
fun and(): Children
128
fun or(): Children
129
fun and(consumer: (Children) -> Unit): Children
130
fun or(consumer: (Children) -> Unit): Children
131
132
// Ordering (for query wrappers)
133
fun orderByAsc(column: KProperty<*>): Children
134
fun orderByDesc(column: KProperty<*>): Children
135
fun orderByAsc(vararg columns: KProperty<*>): Children
136
fun orderByDesc(vararg columns: KProperty<*>): Children
137
138
// Grouping and Having
139
fun groupBy(column: KProperty<*>): Children
140
fun groupBy(vararg columns: KProperty<*>): Children
141
fun having(sqlHaving: String, vararg params: Any?): Children
142
143
// Utility methods
144
abstract fun instance(): Children
145
abstract fun clear(): Children
146
}
147
```
148
149
## Usage Examples
150
151
### Entity Definition
152
153
```kotlin
154
@TableName("user")
155
data class User(
156
@TableId(type = IdType.AUTO)
157
var id: Long? = null,
158
159
var name: String? = null,
160
var email: String? = null,
161
var age: Int? = null,
162
var active: Boolean? = null,
163
var department: String? = null,
164
var salary: BigDecimal? = null,
165
var createdTime: LocalDateTime? = null,
166
var updatedTime: LocalDateTime? = null
167
) : Model<User>() {
168
169
override fun pkVal(): Serializable? = id
170
}
171
```
172
173
### Basic Query Operations
174
175
```kotlin
176
// Using KtQueryWrapper directly
177
val wrapper = KtQueryWrapper<User>()
178
.eq(User::active, true)
179
.gt(User::age, 18)
180
.like(User::name, "John")
181
.orderByDesc(User::createdTime)
182
183
val users = userService.list(wrapper)
184
185
// Using entity instance for initialization
186
val user = User()
187
val wrapper2 = KtQueryWrapper(user)
188
.ne(User::id, user.id)
189
.eq(User::department, "IT")
190
191
// Type-safe property references
192
val activeUsers = userService.list(
193
KtQueryWrapper<User>()
194
.eq(User::active, true)
195
.isNotNull(User::email)
196
.orderByAsc(User::name)
197
)
198
```
199
200
### Service Integration
201
202
```kotlin
203
@Service
204
class UserService : ServiceImpl<UserMapper, User>() {
205
206
// Kotlin-specific query methods
207
fun findActiveUsersByDepartment(department: String): List<User> {
208
return this.ktQuery()
209
.eq(User::active, true)
210
.eq(User::department, department)
211
.orderByAsc(User::name)
212
.list()
213
}
214
215
fun findUsersByAgeRange(minAge: Int, maxAge: Int): List<User> {
216
return this.list(
217
KtQueryWrapper<User>()
218
.between(User::age, minAge, maxAge)
219
.eq(User::active, true)
220
)
221
}
222
223
fun updateUserSalary(userId: Long, newSalary: BigDecimal): Boolean {
224
return this.ktUpdate()
225
.set(User::salary, newSalary)
226
.set(User::updatedTime, LocalDateTime.now())
227
.eq(User::id, userId)
228
.update()
229
}
230
231
fun deactivateInactiveUsers(days: Int): Boolean {
232
val cutoffDate = LocalDateTime.now().minusDays(days.toLong())
233
return this.ktUpdate()
234
.set(User::active, false)
235
.set(User::updatedTime, LocalDateTime.now())
236
.lt(User::createdTime, cutoffDate)
237
.update()
238
}
239
}
240
```
241
242
### Chain Operations
243
244
```kotlin
245
// Query chains with Kotlin extensions
246
val activeUsers = userService.ktQuery()
247
.eq(User::active, true)
248
.ge(User::age, 21)
249
.isNotNull(User::email)
250
.orderByDesc(User::createdTime)
251
.list()
252
253
// Single result query
254
val user = userService.ktQuery()
255
.eq(User::email, "john@example.com")
256
.eq(User::active, true)
257
.one()
258
259
// Optional result
260
val optionalUser = userService.ktQuery()
261
.eq(User::name, "John Doe")
262
.oneOpt()
263
264
if (optionalUser.isPresent) {
265
val user = optionalUser.get()
266
println("Found user: ${user.name}")
267
}
268
269
// Count operations
270
val activeCount = userService.ktQuery()
271
.eq(User::active, true)
272
.count()
273
274
// Existence check
275
val hasAdmins = userService.ktQuery()
276
.eq(User::department, "ADMIN")
277
.eq(User::active, true)
278
.exists()
279
```
280
281
### Update Operations
282
283
```kotlin
284
// Update with conditions
285
val updated = userService.ktUpdate()
286
.set(User::department, "Engineering")
287
.set(User::updatedTime, LocalDateTime.now())
288
.eq(User::department, "IT")
289
.update()
290
291
// Complex update conditions
292
val updated = userService.ktUpdate()
293
.set(User::active, false)
294
.set(User::updatedTime, LocalDateTime.now())
295
.and { wrapper ->
296
wrapper.lt(User::age, 18)
297
.or()
298
.gt(User::age, 65)
299
}
300
.update()
301
302
// Remove operations
303
val removed = userService.ktUpdate()
304
.eq(User::active, false)
305
.isNull(User::email)
306
.remove()
307
```
308
309
### Complex Conditions
310
311
```kotlin
312
// Nested conditions with lambdas
313
val users = userService.ktQuery()
314
.eq(User::active, true)
315
.and { wrapper ->
316
wrapper.eq(User::department, "IT")
317
.or()
318
.eq(User::department, "Engineering")
319
}
320
.or { wrapper ->
321
wrapper.ge(User::salary, BigDecimal("100000"))
322
.eq(User::active, true)
323
}
324
.orderByDesc(User::salary)
325
.list()
326
327
// Range and collection conditions
328
val users = userService.list(
329
KtQueryWrapper<User>()
330
.`in`(User::department, listOf("IT", "HR", "Finance"))
331
.between(User::age, 25, 45)
332
.notIn(User::id, listOf(1L, 2L, 3L))
333
.isNotNull(User::email)
334
)
335
336
// String matching conditions
337
val users = userService.list(
338
KtQueryWrapper<User>()
339
.like(User::name, "John")
340
.likeRight(User::email, "@company.com")
341
.notLike(User::department, "temp")
342
)
343
```
344
345
### Select Specific Fields
346
347
```kotlin
348
// Select specific columns using property references
349
val wrapper = KtQueryWrapper<User>()
350
.select(User::id, User::name, User::email)
351
.eq(User::active, true)
352
.orderByAsc(User::name)
353
354
val users = userService.list(wrapper)
355
356
// Chain wrapper with select
357
val users = userService.ktQuery()
358
.select(User::name, User::department, User::salary)
359
.eq(User::active, true)
360
.gt(User::salary, BigDecimal("50000"))
361
.list()
362
```
363
364
### Pagination with Kotlin
365
366
```kotlin
367
// Paginated query with Kotlin wrapper
368
val page = Page<User>(1, 10)
369
val result = userService.ktQuery()
370
.eq(User::active, true)
371
.ge(User::age, 18)
372
.orderByDesc(User::createdTime)
373
.page(page)
374
375
val users = result.records
376
val total = result.total
377
378
// Pagination with complex conditions
379
val searchPage = userService.page(
380
Page<User>(1, 20),
381
KtQueryWrapper<User>()
382
.and { wrapper ->
383
wrapper.like(User::name, "John")
384
.or()
385
.like(User::email, "john")
386
}
387
.eq(User::active, true)
388
.orderByDesc(User::updatedTime)
389
)
390
```
391
392
### Static Utility Usage
393
394
```kotlin
395
// Using Db utility with Kotlin wrappers
396
val users = Db.ktQuery(User::class.java)
397
.eq(User::active, true)
398
.list()
399
400
val updated = Db.ktUpdate(User::class.java)
401
.set(User::updatedTime, LocalDateTime.now())
402
.eq(User::id, 1L)
403
.update()
404
405
// ChainWrappers with Kotlin
406
val users = ChainWrappers.ktQueryChain(User::class.java)
407
.eq(User::department, "Engineering")
408
.orderByAsc(User::name)
409
.list()
410
411
val updated = ChainWrappers.ktUpdateChain(userMapper)
412
.set(User::active, false)
413
.lt(User::createdTime, LocalDateTime.now().minusYears(1))
414
.update()
415
```
416
417
### Advanced Kotlin Features
418
419
```kotlin
420
// Extension functions for User entity
421
fun User.isAdult(): Boolean = this.age?.let { it >= 18 } ?: false
422
423
fun User.updateActivity(active: Boolean): Boolean {
424
return Db.ktUpdate(User::class.java)
425
.set(User::active, active)
426
.set(User::updatedTime, LocalDateTime.now())
427
.eq(User::id, this.id)
428
.update()
429
}
430
431
fun User.findSimilarUsers(): List<User> {
432
return Db.ktQuery(User::class.java)
433
.eq(User::department, this.department)
434
.eq(User::active, true)
435
.ne(User::id, this.id)
436
.list()
437
}
438
439
// Usage of extension functions
440
val user = userService.getById(1L)
441
if (user.isAdult()) {
442
user.updateActivity(true)
443
val similarUsers = user.findSimilarUsers()
444
}
445
```
446
447
### Data Class Integration
448
449
```kotlin
450
// Leveraging data class features
451
data class UserSearchCriteria(
452
val name: String? = null,
453
val department: String? = null,
454
val minAge: Int? = null,
455
val maxAge: Int? = null,
456
val active: Boolean? = null
457
)
458
459
class UserService : ServiceImpl<UserMapper, User>() {
460
461
fun searchUsers(criteria: UserSearchCriteria): List<User> {
462
val wrapper = KtQueryWrapper<User>()
463
464
criteria.name?.let { wrapper.like(User::name, it) }
465
criteria.department?.let { wrapper.eq(User::department, it) }
466
criteria.minAge?.let { wrapper.ge(User::age, it) }
467
criteria.maxAge?.let { wrapper.le(User::age, it) }
468
criteria.active?.let { wrapper.eq(User::active, it) }
469
470
return this.list(wrapper.orderByDesc(User::createdTime))
471
}
472
473
fun updateUserFields(id: Long, updates: UserUpdateData): Boolean {
474
val wrapper = KtUpdateWrapper<User>()
475
476
updates.name?.let { wrapper.set(User::name, it) }
477
updates.email?.let { wrapper.set(User::email, it) }
478
updates.department?.let { wrapper.set(User::department, it) }
479
updates.salary?.let { wrapper.set(User::salary, it) }
480
481
wrapper.set(User::updatedTime, LocalDateTime.now())
482
.eq(User::id, id)
483
484
return this.update(wrapper)
485
}
486
}
487
488
data class UserUpdateData(
489
val name: String? = null,
490
val email: String? = null,
491
val department: String? = null,
492
val salary: BigDecimal? = null
493
)
494
```
495
496
### Repository Pattern with Kotlin
497
498
```kotlin
499
@Repository
500
class UserRepository {
501
502
fun findActiveUsersByDepartments(departments: List<String>): List<User> {
503
return Db.ktQuery(User::class.java)
504
.eq(User::active, true)
505
.`in`(User::department, departments)
506
.orderByAsc(User::name)
507
.list()
508
}
509
510
fun findHighEarners(threshold: BigDecimal): List<User> {
511
return Db.list(
512
User::class.java,
513
KtQueryWrapper<User>()
514
.ge(User::salary, threshold)
515
.eq(User::active, true)
516
.orderByDesc(User::salary)
517
)
518
}
519
520
fun updateSalariesByDepartment(department: String, multiplier: Double): Boolean {
521
return Db.ktUpdate(User::class.java)
522
.setSql("salary = salary * $multiplier")
523
.set(User::updatedTime, LocalDateTime.now())
524
.eq(User::department, department)
525
.eq(User::active, true)
526
.update()
527
}
528
529
fun deactivateUsersOlderThan(age: Int): Boolean {
530
return Db.ktUpdate(User::class.java)
531
.set(User::active, false)
532
.set(User::updatedTime, LocalDateTime.now())
533
.gt(User::age, age)
534
.update()
535
}
536
537
fun countUsersByDepartment(): Map<String, Long> {
538
val users = Db.list(
539
User::class.java,
540
KtQueryWrapper<User>()
541
.select(User::department)
542
.eq(User::active, true)
543
)
544
545
return users.groupBy { it.department ?: "Unknown" }
546
.mapValues { it.value.size.toLong() }
547
}
548
}
549
```
550
551
### Error Handling in Kotlin
552
553
```kotlin
554
class KotlinUserService : ServiceImpl<UserMapper, User>() {
555
556
fun safeGetUser(id: Long): User? {
557
return try {
558
this.ktQuery()
559
.eq(User::id, id)
560
.eq(User::active, true)
561
.one()
562
} catch (e: Exception) {
563
println("Error fetching user $id: ${e.message}")
564
null
565
}
566
}
567
568
fun findUserOrDefault(email: String, defaultUser: User): User {
569
return try {
570
this.ktQuery()
571
.eq(User::email, email)
572
.eq(User::active, true)
573
.oneOpt()
574
.orElse(defaultUser)
575
} catch (e: Exception) {
576
println("Error finding user by email $email: ${e.message}")
577
defaultUser
578
}
579
}
580
581
fun batchUpdateWithValidation(updates: List<Pair<Long, String>>): Result<Int> {
582
return try {
583
var successCount = 0
584
585
updates.forEach { (id, newName) ->
586
val updated = this.ktUpdate()
587
.set(User::name, newName)
588
.set(User::updatedTime, LocalDateTime.now())
589
.eq(User::id, id)
590
.eq(User::active, true)
591
.update()
592
593
if (updated) successCount++
594
}
595
596
Result.success(successCount)
597
} catch (e: Exception) {
598
Result.failure(e)
599
}
600
}
601
}
602
```
603
604
## Performance and Best Practices
605
606
### 1. Property Reference Caching
607
608
```kotlin
609
// Kotlin property references are cached automatically
610
// But you can store them for repeated use
611
object UserProperties {
612
val ID = User::id
613
val NAME = User::name
614
val EMAIL = User::email
615
val ACTIVE = User::active
616
val CREATED_TIME = User::createdTime
617
}
618
619
// Usage
620
val users = Db.ktQuery(User::class.java)
621
.eq(UserProperties.ACTIVE, true)
622
.orderByDesc(UserProperties.CREATED_TIME)
623
.list()
624
```
625
626
### 2. Type Safety Benefits
627
628
```kotlin
629
// Compile-time safety - these won't compile if properties don't exist
630
val users = userService.ktQuery()
631
.eq(User::active, true) // ✓ Correct
632
.eq(User::name, "John") // ✓ Correct
633
// .eq(User::invalidField, "x") // ✗ Compilation error
634
.list()
635
```
636
637
### 3. Null Safety Integration
638
639
```kotlin
640
// Kotlin's null safety works with the wrappers
641
fun findUsersByOptionalCriteria(
642
name: String?,
643
department: String?,
644
minAge: Int?
645
): List<User> {
646
val wrapper = KtQueryWrapper<User>()
647
.eq(User::active, true)
648
649
// Safe navigation and let
650
name?.let { wrapper.like(User::name, it) }
651
department?.let { wrapper.eq(User::department, it) }
652
minAge?.let { wrapper.ge(User::age, it) }
653
654
return userService.list(wrapper)
655
}
656
```
657
658
The Kotlin extensions provide a more idiomatic and type-safe way to work with MyBatis-Plus in Kotlin applications, leveraging Kotlin's language features for better developer experience and compile-time safety.