[ Spring ] Spring Boot Mybatis++ 2025

news/2025/2/4 16:01:50 标签: spring, spring boot, mybatis, mybatis++, mybatis-plus

文章目录

          • Structure
          • MyBatis++ Controller Abilities
          • Configure Plugins and Repositories
          • Apply Plugins and Add Dependencies
          • MyBatis++ Spring Properties
          • MyBatis++ Application
          • MyBatis++ Beans
          • MyBatis++ Mapper
          • 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

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
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
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
# 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
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
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
}
package x.spring.hello.model

class UserExample {

    var id: String? = null

    var name: String? = null

    var age: Int? = null
}
package x.spring.hello.model

class UserRoleQueryResult {

    var name = ""

    var role = ""
}
MyBatis++ Mapper

mapper sometimes called interface, service or repository in other projects

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

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 }
}

http://www.niftyadmin.cn/n/5841658.html

相关文章

本地部署运行一下deepseek r1尝鲜

2025-01-20正式发布 DeepSeek-R1&#xff0c;并同步开源模型权重。 DeepSeek-R1 遵循 MIT License&#xff0c;允许用户通过蒸馏技术借助 R1 训练其他模型。 DeepSeek-R1 上线API&#xff0c;对用户开放思维链输出&#xff0c;通过设置 modeldeepseek-reasoner 即可调用。 Dee…

第五章:元婴-React用户功能实战

文章目录 登录页面布局JWT 令牌鉴权用户功能实现用户查询页面用户更改状态用户添加页面用户添加页面表单构建用户编辑页面用户编辑表单页面登录页面布局 import React, { useEffect, useState } from react import { Button, Form, Input, message } from antd import style fr…

使用 Grafana 和 Prometheus展现消息队列性能

引言 上篇文章通过JMX提取Kafka数据&#xff0c;本篇文章将通过JDBC存储Kafka性能数据存储于数据库&#xff0c;并通过Grafana 和 Prometheus进行展示&#xff0c;实现开发中常用的可视化监控 1. 环境准备 Kafka&#xff1a;运行中的 Kafka 集群&#xff0c;确保可以…

【Word快速设置论文公式居中编号右对齐】

1. 查看纸张大小 布局 —> 纸张大小 —> 21厘米*29.7厘米 —> 得到宽度为21厘米 2. 查看左右的页边距 布局 —> 页边距 —> 1.57厘米和1.57厘米 3. 计算距离 公式的距离&#xff1a;&#xff08;21-1.57-1.57&#xff09;/2 8.93厘米 编号靠右的距离&…

【教程】微信扫描二维码进入小程序指定页面并携带参数

功能描述 打开微信扫一扫&#xff0c;扫描产品上的二维码&#xff0c;弹出小程序&#xff0c;跳到“邀请用户”页面。解析二维码中的参数&#xff0c;自动填充到页面中的“邀请码”输入框。 操作步骤 首先&#xff0c;要到微信公众平台对扫普通链接二维码打开小程序功能进行配…

MATLAB | 基于长时间序列栅格数据的Mann-Kendall与Pettitt突变检验分析

各位同学好&#xff0c;今天我们将分享在水文气象等领域中常用的两种突变检验方法——Mann-Kendall&#xff08;MK&#xff09;检验和Pettitt检验。由于时间关系&#xff0c;今天我们不详细介绍具体的公式和推导过程&#xff0c;感兴趣的同学可以参考相关文献&#xff0c;如《P…

自然语言生成(NLG)算法模型评估方案的硬件配置、系统架构设计、软件技术栈、实现流程和关键代码

智能化对话中的自然语言生成&#xff08;NLG&#xff09;算法模型评估是一个复杂而多维的过程&#xff0c;它涉及多个评估指标和策略&#xff0c;以确保生成的文本质量、准确性和流畅性。 智能化对话中的NLG算法模型评估是一个涉及多个评估指标和策略的过程。通过选择合适的评估…

【1】快手面试题整理

[1]. 说说int和Integer的区别 int是Java中的基本数据类型&#xff0c;用于存储整数值。它直接在栈内存中存储数值&#xff0c;默认值是0&#xff0c;并且不能为null&#xff0c;本身不带方法。 Integer是int的包装类&#xff0c;属于引用类型。它在堆内存中存储一个对象&…