[ Spring ] Spring Boot Mybatis++ 2025

文章目录

          • Structure
          • [MyBatis++ Controller Abilities](#MyBatis++ Controller Abilities)
          • [Configure Plugins and Repositories](#Configure Plugins and Repositories)
          • [Apply Plugins and Add Dependencies](#Apply Plugins and Add Dependencies)
          • [MyBatis++ Spring Properties](#MyBatis++ Spring Properties)
          • [MyBatis++ Application](#MyBatis++ Application)
          • [MyBatis++ Beans](#MyBatis++ Beans)
          • [MyBatis++ Mapper](#MyBatis++ Mapper)
          • [MyBatis++ Query Builder](#MyBatis++ Query Builder)
Structure

this blog introduce 3 ways using mybatis

  • based on annotationed SQL and Query interfaces : suppored by MyBatis framework

  • based on Query Wrapper : supported by MyBatis Plus framework

    MyBatis Plus provides a easier way to dynamically set condition and updated fields

  • base on Query Condition : combined MyBatis Plus and Kotlin, so called MyBatis++

    MyBatis++ provides a more easier way to build complicated conditions

    and supports update values through an Example bean

MyBatis++ Controller Abilities

this controller present multiple ways to do CURD with MyBatis++

you can choose your favorite ones or those match your current project most comfortably

kotlin 复制代码
package x.spring.hello.controller

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import x.kotlin.commons.serialize.JSON.toJson
import x.kotlin.commons.serialize.JSON.toJsonOrNull
import x.kotlin.commons.string.UUID
import x.spring.hello.model.User
import x.spring.hello.model.UserExample
import x.spring.hello.repository.UserMapper
import x.spring.hello.mybatis.*

@RestController
class UserController {

    @Autowired
    private lateinit var userMapper: UserMapper

    @GetMapping("/01")
    fun selectAll(): String {
        val userList = userMapper.selectAll()
        return userList.toJson()
    }

    @GetMapping("/02")
    fun selectByName(): String {
        val user = userMapper.selectUserByName("Jimmy")
        return user.toJsonOrNull().orEmpty()
    }

    @GetMapping("/03")
    fun selectByCondition(): String {
        val condition = condition { it.eq(User::name, "Jimmy") }
        val users = userMapper.selectList(condition.build())
        return users.toJson()
    }

    @GetMapping("/04")
    fun insert(): String {
        val user = User()
        user.name = UUID.short()
        userMapper.insert(user)
        return user.toJson()
    }

    @GetMapping("/05")
    fun insertOrUpdate(): String {
        val user = User()
        user.id = "1"
        user.name = UUID.short()
        userMapper.insertOrUpdate(user)
        return user.toJson()
    }

    @GetMapping("/06")
    fun updateByCondition(): String {
        val cond1 = condition { it.isNotNull(User::id) }
        val cond2 = condition { it.eq(User::name, "Jimmy") }
        val cond3 = condition { it.gt(User::age, 15) }
        val cond4 = condition {
            it.set(User::name, "Jimmy")
            it.set(User::age, 18)
        }
        val condition = cond1 and cond2 and cond3 attributes cond4
        val count = userMapper.update(condition.build())
        return count.toJson()
    }

    @GetMapping("/07")
    fun updateByEntityAndCondition(): String {
        val entity = User()
        entity.name = "Updated"
        entity.age = 36
        val cond1 = condition { it.isNotNull(User::id) }
        val cond2 = condition { it.like(User::name, "Jimmy") }
        val cond3 = condition { it.gt(User::age, 35) }
        val condition = cond1 and (cond2 or cond3)
        val count = userMapper.update(entity, condition.build())
        return count.toJson()
    }

    @GetMapping("/08")
    fun updateByExampleAndCondition(): String {
        val example = UserExample()
        example.age = 18
        val cond1 = condition { it.isNotNull(User::id) }
        val cond2 = condition { it.like(User::name, "Jimmy") }
        val cond3 = condition { it.gt(User::age, 35) }
        val condition = cond1 and (cond2 or cond3) values example
        val count = userMapper.update(condition.build())
        return count.toJson()
    }

    @GetMapping("/09")
    fun selectCrossTables(): String {
        val userRoles = userMapper.selectUserRole()
        return userRoles.toJson()
    }
}
Configure Plugins and Repositories
kotlin 复制代码
pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositoriesMode = RepositoriesMode.PREFER_SETTINGS
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

buildscript {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

plugins {
    id("org.jetbrains.kotlin.jvm") version "2.0.21" apply false
    id("org.jetbrains.kotlin.kapt") version "2.0.21" apply false
    id("org.jetbrains.kotlin.plugin.spring") version "2.0.21" apply false
    id("org.springframework.boot") version "3.4.1" apply false
}

include("spring-mybatis")
Apply Plugins and Add Dependencies
kotlin 复制代码
plugins {
    id("org.jetbrains.kotlin.jvm")
    id("org.jetbrains.kotlin.kapt")
    id("org.jetbrains.kotlin.plugin.spring")
    id("org.springframework.boot")
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

dependencies {
    val springBootVersion = "3.4.1"
    val springCloudVersion = "4.2.0"
    val springCloudAlibabaVersion = "2023.0.3.2"
    // commons
    api("io.github.hellogoogle2000:kotlin-commons:1.0.19")
    // kotlin
    api("org.jetbrains.kotlin:kotlin-reflect:2.0.21")
    // spring
    api("org.springframework.boot:spring-boot-starter:$springBootVersion")
    api("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
    api("org.springframework.cloud:spring-cloud-starter-bootstrap:$springCloudVersion")
    // mybatis
    api("link.thingscloud:quick-spring-boot-starter-mybatis-plus:2025.01.22")
}
MyBatis++ Spring Properties
properties 复制代码
# service
server.port=10003
spring.application.name=mybatis
spring.profiles.active=dev
spring.devtools.add-properties=false
# mybatis
spring.datasource.username=root
spring.datasource.password=123456789
spring.datasource.url=jdbc:mysql://localhost:3306/dev?characterEncoding=utf-8&serverTimezone=UTC
MyBatis++ Application
kotlin 复制代码
package x.spring.hello

import org.mybatis.spring.annotation.MapperScan
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
@MapperScan(basePackages = ["x.spring.hello.repository"])
class MybatisApplication

fun main(args: Array<String>) {
    runApplication<MybatisApplication>(*args)
}
MyBatis++ Beans
kotlin 复制代码
package x.spring.hello.model

import com.baomidou.mybatisplus.annotation.IdType
import com.baomidou.mybatisplus.annotation.TableId

class User {

    @TableId(type = IdType.ASSIGN_UUID)
    var id = ""

    var name = ""

    var age = 0
}
kotlin 复制代码
package x.spring.hello.model

class UserExample {

    var id: String? = null

    var name: String? = null

    var age: Int? = null
}
kotlin 复制代码
package x.spring.hello.model

class UserRoleQueryResult {

    var name = ""

    var role = ""
}
MyBatis++ Mapper

mapper sometimes called interface, service or repository in other projects

kotlin 复制代码
package x.spring.hello.repository

import link.thingscloud.quick.mybatisplus.base.BaseMapper
import org.apache.ibatis.annotations.Select
import x.spring.hello.model.User
import x.spring.hello.model.UserRoleQueryResult

interface UserMapper : BaseMapper<User> {

    @Select("select * from user")
    fun selectAll(): MutableList<User>

    @Select("select * from user where name = #{name}")
    fun selectUserByName(name: String): User?

    @Select(
        """
          select 
            user.name as name,
            role.name as role 
          from user left join role
          on user.roleId = role.id
        """
    )
    fun selectUserRole(): List<UserRoleQueryResult>
}
MyBatis++ Query Builder

this is the core component to build query condition and examples

difference between entity and example is :

entity will update all field, while example only update non-null fields

kotlin 复制代码
package x.spring.hello.mybatis

import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties

fun interface ConditionConfigurator<T : Any> {
    fun configure(wrapper: KtUpdateWrapper<T>)
}

data class QueryCondition<T : Any>(
    val configurator: ConditionConfigurator<T>
)

inline fun <reified T : Any> QueryCondition<T>.build(): KtUpdateWrapper<T> {
    val wrapper = KtUpdateWrapper(T::class.java)
    configurator.configure(wrapper)
    return wrapper
}

inline fun <reified T : Any> condition(configurator: ConditionConfigurator<T>): QueryCondition<T> {
    return QueryCondition(configurator)
}

infix fun <T : Any> QueryCondition<T>.and(other: QueryCondition<T>): QueryCondition<T> {
    val configurator = ConditionConfigurator {
        configurator.configure(it)
        it.and { other.configurator.configure(it) }
    }
    return QueryCondition(configurator)
}

infix fun <T : Any> QueryCondition<T>.or(other: QueryCondition<T>): QueryCondition<T> {
    val configurator = ConditionConfigurator {
        configurator.configure(it)
        it.or { other.configurator.configure(it) }
    }
    return QueryCondition(configurator)
}

infix fun <T : Any> QueryCondition<T>.not(other: QueryCondition<T>): QueryCondition<T> {
    val configurator = ConditionConfigurator {
        configurator.configure(it)
        it.not { other.configurator.configure(it) }
    }
    return QueryCondition(configurator)
}

infix fun <T : Any> QueryCondition<T>.attributes(other: QueryCondition<T>): QueryCondition<T> {
    val configurator = ConditionConfigurator {
        configurator.configure(it)
        other.configurator.configure(it)
    }
    return QueryCondition(configurator)
}

inline infix fun <reified T : Any, reified S : Any> QueryCondition<T>.values(example: S): QueryCondition<T> {
    val configurator = ConditionConfigurator { wrapper ->
        configurator.configure(wrapper)
        val properties = S::class.memberProperties
        properties.forEach { propertyS ->
            val value = propertyS.get(example)
            value.takeIf { it != null } ?: return@forEach
            val property = T::class.findPropertyByName(propertyS.name)
            property.takeIf { it != null } ?: return@forEach
            wrapper.set(property, value)
        }
    }
    return QueryCondition(configurator)
}

inline fun <reified T : Any> KClass<T>.findPropertyByName(name: String): KProperty1<T, *>? {
    return memberProperties.firstOrNull { it.name == name }
}
相关推荐
Java 码农2 分钟前
Spring Cloud Eureka 的实现原理
spring·spring cloud·eureka
一线大码9 分钟前
SpringBoot 优雅实现接口的多实现类方式
java·spring boot·后端
Q_Q196328847527 分钟前
python+uniapp基于微信小程序的助眠小程序
spring boot·python·小程序·django·flask·uni-app·node.js
摇滚侠37 分钟前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 属性优先级 行内写法 变量选择 笔记42
java·spring boot·笔记
摇滚侠41 分钟前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 总结 热部署 常用配置 笔记44
java·spring boot·笔记
十年小站41 分钟前
一、新建一个SpringBoot3项目
java·spring boot
程序员阿达1 小时前
开题报告之基于SpringBoot框架的路面故障信息上报系统设计与实现
java·spring boot·后端
哞哞不熬夜1 小时前
JavaEE--SpringIoC
java·开发语言·spring boot·spring·java-ee·maven
gordon~92 小时前
Spring 的bean是安全的吗
java·安全·spring·bean
疯癫的老码农2 小时前
【Linux环境下安装】SpringBoot应用环境安装(五)-milvus安装
linux·spring boot·milvus