CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-baomidou--mybatis-plus-extension

MyBatis-Plus Extension module providing advanced features including service layers, Kotlin extensions, SQL parsers, caching mechanisms, and various interceptors for enhanced ORM functionality

Pending
Overview
Eval results
Files

kotlin-extensions.mddocs/

Kotlin Extensions

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.

Core Components

KtQueryWrapper

Type-safe query wrapper for Kotlin with property-based field references.

class KtQueryWrapper<T>(entity: T? = null) : AbstractKtWrapper<T, KtQueryWrapper<T>>() {
    constructor(entityClass: Class<T>) : this(null)
    
    // Select operations
    fun select(vararg columns: KProperty<*>): KtQueryWrapper<T>
    fun getSqlSelect(): String
    
    // Factory methods
    fun instance(): KtQueryWrapper<T>
    fun clear(): KtQueryWrapper<T>
    
    // Inherited condition methods from AbstractKtWrapper
    // eq, ne, gt, ge, lt, le, like, notLike, in, notIn, etc.
}

KtUpdateWrapper

Type-safe update wrapper for Kotlin operations.

class KtUpdateWrapper<T>(entity: T? = null) : AbstractKtWrapper<T, KtUpdateWrapper<T>>() {
    constructor(entityClass: Class<T>) : this(null)
    
    // Set operations using Kotlin properties
    fun set(column: KProperty<*>, value: Any?): KtUpdateWrapper<T>
    fun setSql(sql: String): KtUpdateWrapper<T>
    
    // Factory methods
    fun instance(): KtUpdateWrapper<T>  
    fun clear(): KtUpdateWrapper<T>
}

KtQueryChainWrapper

Kotlin chain wrapper for fluent query operations.

class KtQueryChainWrapper<T> : AbstractChainWrapper<T, KProperty<*>, KtQueryChainWrapper<T>, KtQueryWrapper<T>>(),
    ChainQuery<T> {
    
    constructor(baseMapper: BaseMapper<T>)
    constructor(entityClass: Class<T>)
    
    // Terminal operations
    override fun list(): List<T>
    override fun one(): T
    override fun oneOpt(): Optional<T>
    override fun count(): Long
    override fun exists(): Boolean
    override fun <E : IPage<T>> page(page: E): E
    
    // Select operations
    fun select(vararg columns: KProperty<*>): KtQueryChainWrapper<T>
}

KtUpdateChainWrapper

Kotlin chain wrapper for fluent update operations.

class KtUpdateChainWrapper<T> : AbstractChainWrapper<T, KProperty<*>, KtUpdateChainWrapper<T>, KtUpdateWrapper<T>>(),
    ChainUpdate<T> {
    
    constructor(baseMapper: BaseMapper<T>)
    constructor(entityClass: Class<T>)
    
    // Terminal operations
    override fun update(): Boolean
    override fun update(entity: T): Boolean
    override fun remove(): Boolean
    
    // Set operations
    fun set(column: KProperty<*>, value: Any?): KtUpdateChainWrapper<T>
    fun setSql(sql: String): KtUpdateChainWrapper<T>
}

AbstractKtWrapper

Base class for Kotlin wrappers providing type-safe condition methods.

abstract class AbstractKtWrapper<T, Children : AbstractKtWrapper<T, Children>> {
    
    // Comparison conditions
    fun eq(column: KProperty<*>, value: Any?): Children
    fun ne(column: KProperty<*>, value: Any?): Children
    fun gt(column: KProperty<*>, value: Any?): Children
    fun ge(column: KProperty<*>, value: Any?): Children
    fun lt(column: KProperty<*>, value: Any?): Children
    fun le(column: KProperty<*>, value: Any?): Children
    
    // String conditions
    fun like(column: KProperty<*>, value: Any?): Children
    fun notLike(column: KProperty<*>, value: Any?): Children
    fun likeLeft(column: KProperty<*>, value: Any?): Children
    fun likeRight(column: KProperty<*>, value: Any?): Children
    
    // Null conditions
    fun isNull(column: KProperty<*>): Children
    fun isNotNull(column: KProperty<*>): Children
    
    // Collection conditions
    fun `in`(column: KProperty<*>, values: Collection<*>): Children
    fun notIn(column: KProperty<*>, values: Collection<*>): Children
    fun `in`(column: KProperty<*>, vararg values: Any?): Children
    fun notIn(column: KProperty<*>, vararg values: Any?): Children
    
    // Range conditions
    fun between(column: KProperty<*>, val1: Any?, val2: Any?): Children
    fun notBetween(column: KProperty<*>, val1: Any?, val2: Any?): Children
    
    // Logical operators
    fun and(): Children
    fun or(): Children
    fun and(consumer: (Children) -> Unit): Children
    fun or(consumer: (Children) -> Unit): Children
    
    // Ordering (for query wrappers)
    fun orderByAsc(column: KProperty<*>): Children
    fun orderByDesc(column: KProperty<*>): Children
    fun orderByAsc(vararg columns: KProperty<*>): Children
    fun orderByDesc(vararg columns: KProperty<*>): Children
    
    // Grouping and Having
    fun groupBy(column: KProperty<*>): Children
    fun groupBy(vararg columns: KProperty<*>): Children
    fun having(sqlHaving: String, vararg params: Any?): Children
    
    // Utility methods
    abstract fun instance(): Children
    abstract fun clear(): Children
}

Usage Examples

Entity Definition

@TableName("user")
data class User(
    @TableId(type = IdType.AUTO)
    var id: Long? = null,
    
    var name: String? = null,
    var email: String? = null,
    var age: Int? = null,
    var active: Boolean? = null,
    var department: String? = null,
    var salary: BigDecimal? = null,
    var createdTime: LocalDateTime? = null,
    var updatedTime: LocalDateTime? = null
) : Model<User>() {
    
    override fun pkVal(): Serializable? = id
}

Basic Query Operations

// Using KtQueryWrapper directly
val wrapper = KtQueryWrapper<User>()
    .eq(User::active, true)
    .gt(User::age, 18)
    .like(User::name, "John")
    .orderByDesc(User::createdTime)

val users = userService.list(wrapper)

// Using entity instance for initialization
val user = User()
val wrapper2 = KtQueryWrapper(user)
    .ne(User::id, user.id)
    .eq(User::department, "IT")

// Type-safe property references
val activeUsers = userService.list(
    KtQueryWrapper<User>()
        .eq(User::active, true)
        .isNotNull(User::email)
        .orderByAsc(User::name)
)

Service Integration

@Service
class UserService : ServiceImpl<UserMapper, User>() {
    
    // Kotlin-specific query methods
    fun findActiveUsersByDepartment(department: String): List<User> {
        return this.ktQuery()
            .eq(User::active, true)
            .eq(User::department, department)
            .orderByAsc(User::name)
            .list()
    }
    
    fun findUsersByAgeRange(minAge: Int, maxAge: Int): List<User> {
        return this.list(
            KtQueryWrapper<User>()
                .between(User::age, minAge, maxAge)
                .eq(User::active, true)
        )
    }
    
    fun updateUserSalary(userId: Long, newSalary: BigDecimal): Boolean {
        return this.ktUpdate()
            .set(User::salary, newSalary)
            .set(User::updatedTime, LocalDateTime.now())
            .eq(User::id, userId)
            .update()
    }
    
    fun deactivateInactiveUsers(days: Int): Boolean {
        val cutoffDate = LocalDateTime.now().minusDays(days.toLong())
        return this.ktUpdate()
            .set(User::active, false)
            .set(User::updatedTime, LocalDateTime.now())
            .lt(User::createdTime, cutoffDate)
            .update()
    }
}

Chain Operations

// Query chains with Kotlin extensions
val activeUsers = userService.ktQuery()
    .eq(User::active, true)
    .ge(User::age, 21)
    .isNotNull(User::email)
    .orderByDesc(User::createdTime)
    .list()

// Single result query
val user = userService.ktQuery()
    .eq(User::email, "john@example.com")
    .eq(User::active, true)
    .one()

// Optional result
val optionalUser = userService.ktQuery()
    .eq(User::name, "John Doe")
    .oneOpt()

if (optionalUser.isPresent) {
    val user = optionalUser.get()
    println("Found user: ${user.name}")
}

// Count operations
val activeCount = userService.ktQuery()
    .eq(User::active, true)
    .count()

// Existence check
val hasAdmins = userService.ktQuery()
    .eq(User::department, "ADMIN")
    .eq(User::active, true)
    .exists()

Update Operations

// Update with conditions
val updated = userService.ktUpdate()
    .set(User::department, "Engineering")
    .set(User::updatedTime, LocalDateTime.now())
    .eq(User::department, "IT")
    .update()

// Complex update conditions
val updated = userService.ktUpdate()
    .set(User::active, false)
    .set(User::updatedTime, LocalDateTime.now())
    .and { wrapper ->
        wrapper.lt(User::age, 18)
            .or()
            .gt(User::age, 65)
    }
    .update()

// Remove operations
val removed = userService.ktUpdate()
    .eq(User::active, false)
    .isNull(User::email)
    .remove()

Complex Conditions

// Nested conditions with lambdas
val users = userService.ktQuery()
    .eq(User::active, true)
    .and { wrapper ->
        wrapper.eq(User::department, "IT")
            .or()
            .eq(User::department, "Engineering")
    }
    .or { wrapper ->
        wrapper.ge(User::salary, BigDecimal("100000"))
            .eq(User::active, true)
    }
    .orderByDesc(User::salary)
    .list()

// Range and collection conditions
val users = userService.list(
    KtQueryWrapper<User>()
        .`in`(User::department, listOf("IT", "HR", "Finance"))
        .between(User::age, 25, 45)
        .notIn(User::id, listOf(1L, 2L, 3L))
        .isNotNull(User::email)
)

// String matching conditions
val users = userService.list(
    KtQueryWrapper<User>()
        .like(User::name, "John")
        .likeRight(User::email, "@company.com")
        .notLike(User::department, "temp")
)

Select Specific Fields

// Select specific columns using property references
val wrapper = KtQueryWrapper<User>()
    .select(User::id, User::name, User::email)
    .eq(User::active, true)
    .orderByAsc(User::name)

val users = userService.list(wrapper)

// Chain wrapper with select
val users = userService.ktQuery()
    .select(User::name, User::department, User::salary)
    .eq(User::active, true)
    .gt(User::salary, BigDecimal("50000"))
    .list()

Pagination with Kotlin

// Paginated query with Kotlin wrapper
val page = Page<User>(1, 10)
val result = userService.ktQuery()
    .eq(User::active, true)
    .ge(User::age, 18)
    .orderByDesc(User::createdTime)
    .page(page)

val users = result.records
val total = result.total

// Pagination with complex conditions
val searchPage = userService.page(
    Page<User>(1, 20),
    KtQueryWrapper<User>()
        .and { wrapper ->
            wrapper.like(User::name, "John")
                .or()
                .like(User::email, "john")
        }
        .eq(User::active, true)
        .orderByDesc(User::updatedTime)
)

Static Utility Usage

// Using Db utility with Kotlin wrappers
val users = Db.ktQuery(User::class.java)
    .eq(User::active, true)
    .list()

val updated = Db.ktUpdate(User::class.java)
    .set(User::updatedTime, LocalDateTime.now())
    .eq(User::id, 1L)
    .update()

// ChainWrappers with Kotlin
val users = ChainWrappers.ktQueryChain(User::class.java)
    .eq(User::department, "Engineering")
    .orderByAsc(User::name)
    .list()

val updated = ChainWrappers.ktUpdateChain(userMapper)
    .set(User::active, false)
    .lt(User::createdTime, LocalDateTime.now().minusYears(1))
    .update()

Advanced Kotlin Features

// Extension functions for User entity
fun User.isAdult(): Boolean = this.age?.let { it >= 18 } ?: false

fun User.updateActivity(active: Boolean): Boolean {
    return Db.ktUpdate(User::class.java)
        .set(User::active, active)
        .set(User::updatedTime, LocalDateTime.now())
        .eq(User::id, this.id)
        .update()
}

fun User.findSimilarUsers(): List<User> {
    return Db.ktQuery(User::class.java)
        .eq(User::department, this.department)
        .eq(User::active, true)
        .ne(User::id, this.id)
        .list()
}

// Usage of extension functions
val user = userService.getById(1L)
if (user.isAdult()) {
    user.updateActivity(true)
    val similarUsers = user.findSimilarUsers()
}

Data Class Integration

// Leveraging data class features
data class UserSearchCriteria(
    val name: String? = null,
    val department: String? = null,
    val minAge: Int? = null,
    val maxAge: Int? = null,
    val active: Boolean? = null
)

class UserService : ServiceImpl<UserMapper, User>() {
    
    fun searchUsers(criteria: UserSearchCriteria): List<User> {
        val wrapper = KtQueryWrapper<User>()
        
        criteria.name?.let { wrapper.like(User::name, it) }
        criteria.department?.let { wrapper.eq(User::department, it) }
        criteria.minAge?.let { wrapper.ge(User::age, it) }
        criteria.maxAge?.let { wrapper.le(User::age, it) }
        criteria.active?.let { wrapper.eq(User::active, it) }
        
        return this.list(wrapper.orderByDesc(User::createdTime))
    }
    
    fun updateUserFields(id: Long, updates: UserUpdateData): Boolean {
        val wrapper = KtUpdateWrapper<User>()
        
        updates.name?.let { wrapper.set(User::name, it) }
        updates.email?.let { wrapper.set(User::email, it) }
        updates.department?.let { wrapper.set(User::department, it) }
        updates.salary?.let { wrapper.set(User::salary, it) }
        
        wrapper.set(User::updatedTime, LocalDateTime.now())
            .eq(User::id, id)
        
        return this.update(wrapper)
    }
}

data class UserUpdateData(
    val name: String? = null,
    val email: String? = null,
    val department: String? = null,
    val salary: BigDecimal? = null
)

Repository Pattern with Kotlin

@Repository
class UserRepository {
    
    fun findActiveUsersByDepartments(departments: List<String>): List<User> {
        return Db.ktQuery(User::class.java)
            .eq(User::active, true)
            .`in`(User::department, departments)
            .orderByAsc(User::name)
            .list()
    }
    
    fun findHighEarners(threshold: BigDecimal): List<User> {
        return Db.list(
            User::class.java,
            KtQueryWrapper<User>()
                .ge(User::salary, threshold)
                .eq(User::active, true)
                .orderByDesc(User::salary)
        )
    }
    
    fun updateSalariesByDepartment(department: String, multiplier: Double): Boolean {
        return Db.ktUpdate(User::class.java)
            .setSql("salary = salary * $multiplier")
            .set(User::updatedTime, LocalDateTime.now())
            .eq(User::department, department)
            .eq(User::active, true)
            .update()
    }
    
    fun deactivateUsersOlderThan(age: Int): Boolean {
        return Db.ktUpdate(User::class.java)
            .set(User::active, false)
            .set(User::updatedTime, LocalDateTime.now())
            .gt(User::age, age)
            .update()
    }
    
    fun countUsersByDepartment(): Map<String, Long> {
        val users = Db.list(
            User::class.java,
            KtQueryWrapper<User>()
                .select(User::department)
                .eq(User::active, true)
        )
        
        return users.groupBy { it.department ?: "Unknown" }
            .mapValues { it.value.size.toLong() }
    }
}

Error Handling in Kotlin

class KotlinUserService : ServiceImpl<UserMapper, User>() {
    
    fun safeGetUser(id: Long): User? {
        return try {
            this.ktQuery()
                .eq(User::id, id)
                .eq(User::active, true)
                .one()
        } catch (e: Exception) {
            println("Error fetching user $id: ${e.message}")
            null
        }
    }
    
    fun findUserOrDefault(email: String, defaultUser: User): User {
        return try {
            this.ktQuery()
                .eq(User::email, email)
                .eq(User::active, true)
                .oneOpt()
                .orElse(defaultUser)
        } catch (e: Exception) {
            println("Error finding user by email $email: ${e.message}")
            defaultUser
        }
    }
    
    fun batchUpdateWithValidation(updates: List<Pair<Long, String>>): Result<Int> {
        return try {
            var successCount = 0
            
            updates.forEach { (id, newName) ->
                val updated = this.ktUpdate()
                    .set(User::name, newName)
                    .set(User::updatedTime, LocalDateTime.now())
                    .eq(User::id, id)
                    .eq(User::active, true)
                    .update()
                
                if (updated) successCount++
            }
            
            Result.success(successCount)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

Performance and Best Practices

1. Property Reference Caching

// Kotlin property references are cached automatically
// But you can store them for repeated use
object UserProperties {
    val ID = User::id
    val NAME = User::name
    val EMAIL = User::email
    val ACTIVE = User::active
    val CREATED_TIME = User::createdTime
}

// Usage
val users = Db.ktQuery(User::class.java)
    .eq(UserProperties.ACTIVE, true)
    .orderByDesc(UserProperties.CREATED_TIME)
    .list()

2. Type Safety Benefits

// Compile-time safety - these won't compile if properties don't exist
val users = userService.ktQuery()
    .eq(User::active, true)        // ✓ Correct
    .eq(User::name, "John")        // ✓ Correct
    // .eq(User::invalidField, "x") // ✗ Compilation error
    .list()

3. Null Safety Integration

// Kotlin's null safety works with the wrappers
fun findUsersByOptionalCriteria(
    name: String?,
    department: String?,
    minAge: Int?
): List<User> {
    val wrapper = KtQueryWrapper<User>()
        .eq(User::active, true)
    
    // Safe navigation and let
    name?.let { wrapper.like(User::name, it) }
    department?.let { wrapper.eq(User::department, it) }
    minAge?.let { wrapper.ge(User::age, it) }
    
    return userService.list(wrapper)
}

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.

Install with Tessl CLI

npx tessl i tessl/maven-com-baomidou--mybatis-plus-extension

docs

active-record.md

condition-builders.md

index.md

kotlin-extensions.md

pagination.md

plugin-system.md

service-layer.md

static-utilities.md

tile.json