Compare commits

..

5 Commits

Author SHA1 Message Date
00c1be6387
Merge branch 'master' of git.mingliqiye.com:minglipro/mingli-utils 2026-01-08 13:21:38 +08:00
2063d86097
feat(core): 将项目从Java迁移到Kotlin并升级依赖
- 将多个Java文件转换为Kotlin文件,包括CallType、QuickBaseTypeHandler等
- 升级Netty版本从4.2.9.Final到4.130.Final
- 添加新的Netty重连调度器ClientScheduleReconnect功能
- 新增DateTime和MySQL UUID类型处理器支持
- 创建命名线程工厂NamedThreadFactory用于线程池管理
- 修复UUID转换方法名称错误
- 更新项目版本号从4.2.7到4.3.2
- 移除Java版本的ValueGetter类,使用Kotlin数据类替代
2026-01-08 13:21:02 +08:00
2db24530d6
feat(bytes): 添加ByteBuffer工具类和ByteFlags数据结构
- 新增ByteBufferUtils.kt文件,提供字符串、布尔值、位标志等类型的读写扩展函数
- 新增ByteFlags.kt文件,实现8位布尔标志集合的数据结构和位操作功能
- 添加putString/getString方法支持C风格字符串的null终止符处理
- 实现ByteFlags的位操作功能,包括AND、OR、XOR、NOT等运算
- 添加二进制字符串、十六进制字符串的转换和格式化输出功能
- 移除NettyUtils中重复的toByteBuffer扩展函数
- 更新项目版本号从4.2.6到4.2.7
2026-01-07 10:07:48 +08:00
2bcd4b329c
feat(network): 增强网络地址端口处理功能
- 添加IPv4地址与字节数组相互转换功能
- 实现ByteBuffer与网络地址端口的读写操作
- 增加网络端点字节数组序列化支持
- 添加Netty框架ByteBuf转换工具类
- 更新项目版本号至4.2.6
- 修改依赖引入方式为compileOnly
- 移除未使用的SuperStream导入
- 添加UUID类ByteBuffer读写功能
- 创建新的测试主函数验证功能
2026-01-06 14:36:13 +08:00
a081744f14
refactor(mybatis):重构类型处理器实现以提升可维护性
移除 Kotlin 编写的 DateTimeTypeHandler 和 UUID 相关类型处理器,
新增 Java 编写的通用 QuickBaseTypeHandler 抽象类及辅助类,统一处理 MyBatis 类型转换逻辑。同时更新 UUID 类的 toString 方法,
增强不同版本 UUID 的信息展示。升级项目版本至 4.2.4。
2025-09-21 21:55:43 +08:00
20 changed files with 1477 additions and 499 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils
* CurrentFile build.gradle.kts
* LastUpdate 2025-09-21 15:36:59
* LastUpdate 2026-01-08 11:10:03
* UpdateUser MingLiPro
*/
@ -69,14 +69,14 @@ dependencies {
implementation("org.slf4j:slf4j-api:2.0.17")
implementation("com.mingliqiye.utils.jna:WinKernel32Api:1.0.1")
// https://github.com/jeremyh/jBCrypt
implementation("org.mindrot:jbcrypt:0.4")
compileOnly("org.mindrot:jbcrypt:0.4")
compileOnly("org.springframework.boot:spring-boot-starter:2.7.14")
compileOnly("com.fasterxml.jackson.core:jackson-databind:2.19.2")
compileOnly("com.google.code.gson:gson:2.13.1")
compileOnly("org.mybatis:mybatis:3.5.19")
compileOnly("com.alibaba.fastjson2:fastjson2:2.0.58")
compileOnly("io.netty:netty-all:4.1.130.Final")
compileOnly("com.baomidou:mybatis-plus-core:3.0.1")
compileOnly("net.java.dev.jna:jna:5.17.0")

View File

@ -1,5 +1,5 @@
#
# Copyright 2025 mingliqiye
# Copyright 2026 mingliqiye
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -16,13 +16,13 @@
# ProjectName mingli-utils
# ModuleName mingli-utils
# CurrentFile gradle.properties
# LastUpdate 2025-09-21 15:38:52
# LastUpdate 2026-01-08 13:20:25
# UpdateUser MingLiPro
#
JDKVERSIONS=1.8
GROUPSID=com.mingliqiye.utils
ARTIFACTID=mingli-utils
VERSIONS=4.1.9
VERSIONS=4.3.2
signing.keyId=B22AA93B
signing.password=
signing.secretKeyRingFile=secret.gpg

View File

@ -0,0 +1,42 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Main.kt
* LastUpdate 2026-01-06 14:04:14
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils
import com.mingliqiye.utils.network.NetworkEndpoint
import com.mingliqiye.utils.uuid.UUID
import java.nio.ByteBuffer
fun main() {
val byteBuffer = ByteBuffer.allocate(320)
NetworkEndpoint
.of("0:65532")
.writeIpv4toByteBuffer(byteBuffer)
UUID.getMaxUUID().writeToByteBuffer(byteBuffer)
byteBuffer.flip()
println(NetworkEndpoint.ofIpv4(byteBuffer))
println(UUID.of(byteBuffer))
}

View File

@ -0,0 +1,205 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ByteBufferUtils.kt
* LastUpdate 2026-01-07 10:01:45
* UpdateUser MingLiPro
*/
@file:JvmName("ByteBufferUtil")
package com.mingliqiye.utils.bytes
import java.nio.ByteBuffer
import java.nio.charset.Charset
/**
* 将字符串以指定字符集编码后写入 [ByteBuffer]并在末尾追加一个空字节`0x00`作为字符串终止符
*
* 此方法适用于需要 C 风格字符串null-terminated string的协议或场景
*
* @param string 要写入的字符串不可为 null
* @param charset 字符编码默认为 [Charsets.UTF_8]
* @return 当前 [ByteBuffer] 实例支持链式调用
*/
fun ByteBuffer.putString(string: String, charset: Charset = Charsets.UTF_8): ByteBuffer =
this.put(string.toByteArray(charset)).put(0x00)
/**
* [ByteBuffer] 中读取一个以空字节`0x00`结尾的字符串
*
* 该方法会持续读取字节直到遇到第一个 `0x00`null terminator为止并将之前读取的字节按指定字符集解码为字符串
* **注意**此操作会修改缓冲区的位置position且不处理缓冲区越界情况若无终止符会抛出异常
*
* @param charset 字符编码默认为 [Charsets.UTF_8]
* @return 解码后的字符串不含终止符
* @throws BufferUnderflowException 如果缓冲区中没有足够的字节即未找到终止符
*/
fun ByteBuffer.getString(charset: Charset = Charsets.UTF_8): String {
val data = ArrayList<Byte>()
while (true) {
val byte = this.get()
if (byte == 0x00.toByte()) {
break
}
data.add(byte)
}
return String(data.toByteArray(), charset)
}
/**
* [ByteBuffer] 中剩余的全部字节读取为一个 [ByteArray]
*
* 此方法会读取从当前位置到 limit 之间的所有字节并推进 position
*
* @return 包含剩余字节的新字节数组
*/
fun ByteBuffer.toByteArray(): ByteArray {
val ba = ByteArray(this.remaining())
this.get(ba)
return ba
}
/**
* 将一个布尔列表长度必须为 8转换为 [ByteFlags] 并写入 [ByteBuffer]
*
* 列表索引 0~7 分别对应位 b0~b7b0 为最低有效位
*
* @param bit 长度为 8 的布尔列表表示 8 个位的状态
* @return 当前 [ByteBuffer] 实例支持链式调用
* @throws IndexOutOfBoundsException 如果列表长度不足 8
*/
fun ByteBuffer.putByteFlags(
bit: List<Boolean> = List(8) { false },
): ByteBuffer = this.putByteFlags(
ByteFlags(
bit[0],
bit[1],
bit[2],
bit[3],
bit[4],
bit[5],
bit[6],
bit[7],
)
)
/**
* 将一个布尔数组长度必须为 8转换为 [ByteFlags] 并写入 [ByteBuffer]
*
* 数组索引 0~7 分别对应位 b0~b7b0 为最低有效位
*
* @param bit 长度为 8 的布尔数组
* @return 当前 [ByteBuffer] 实例支持链式调用
* @throws IndexOutOfBoundsException 如果数组长度不足 8
*/
fun ByteBuffer.putByteFlags(
bit: BooleanArray = BooleanArray(8),
): ByteBuffer = this.putByteFlags(
ByteFlags(
bit[0],
bit[1],
bit[2],
bit[3],
bit[4],
bit[5],
bit[6],
bit[7],
)
)
/**
* [ByteFlags] 对象转换为字节并写入 [ByteBuffer]
*
* @param byteFlags 要写入的位标志对象
* @return 当前 [ByteBuffer] 实例支持链式调用
*/
fun ByteBuffer.putByteFlags(byteFlags: ByteFlags): ByteBuffer = this.put(byteFlags.toByte())
/**
* 通过 8 个独立的布尔参数构造 [ByteFlags] 并写入 [ByteBuffer]
*
* 参数顺序`bit0`LSB `bit7`MSB
*
* @param bit0 0 最低位
* @param bit1 1
* @param bit2 2
* @param bit3 3
* @param bit4 4
* @param bit5 5
* @param bit6 6
* @param bit7 7 最高位
* @return 当前 [ByteBuffer] 实例支持链式调用
*/
fun ByteBuffer.putByteFlags(
bit0: Boolean = false,
bit1: Boolean = false,
bit2: Boolean = false,
bit3: Boolean = false,
bit4: Boolean = false,
bit5: Boolean = false,
bit6: Boolean = false,
bit7: Boolean = false,
): ByteBuffer = this.putByteFlags(
ByteFlags(
bit0,
bit1,
bit2,
bit3,
bit4,
bit5,
bit6,
bit7,
)
)
/**
* [ByteBuffer] 中读取一个字节并将其解析为 [ByteFlags] 对象
*
* @return 表示该字节各位状态的 [ByteFlags] 实例
*/
fun ByteBuffer.getByteFlags(): ByteFlags = ByteFlags(this.get())
/**
* [ByteBuffer] 中读取一个字节并将其解释为布尔值
*
* 约定`0x01` 表示 `true`其他值包括 `0x00`均视为 `false`
*
* @return 布尔值
*/
fun ByteBuffer.getBoolean(): Boolean = this.get() == 0x01.toByte()
/**
* 将布尔值写入 [ByteBuffer]
*
* 约定`true` 写为 `0x01``false` 写为 `0x00`
*
* @param boolean 要写入的布尔值
* @return 当前 [ByteBuffer] 实例支持链式调用
*/
fun ByteBuffer.putBoolean(boolean: Boolean): ByteBuffer = this.put(if (boolean) 0x01 else 0x00)
/**
* 将当前字节数组包装为一个只读的 [ByteBuffer]
*
* 使用 [ByteBuffer.wrap] 创建position 初始为 0limit 为数组长度
*
* @return 包装后的 [ByteBuffer] 实例
*/
fun ByteArray.toByteBuffer(): ByteBuffer {
return ByteBuffer.wrap(this)
}

View File

@ -0,0 +1,460 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ByteFlags.kt
* LastUpdate 2026-01-07 09:37:09
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.bytes
/**
* 表示一个字节8 的布尔标志集合
*
* 每一位对应一个布尔值其中
* - `b0` 表示最低有效位LSBbit 0值为 0x01
* - `b7` 表示最高有效位MSBbit 7值为 0x80
*
* 该类支持从字节布尔数组二进制字符串等构造并提供位操作状态查询和格式化输出等功能
*/
data class ByteFlags(
/** 第 0 位最低位0x01 */
var b0: Boolean = false,
/** 第 1 位0x02 */
var b1: Boolean = false,
/** 第 2 位0x04 */
var b2: Boolean = false,
/** 第 3 位0x08 */
var b3: Boolean = false,
/** 第 4 位0x10 */
var b4: Boolean = false,
/** 第 5 位0x20 */
var b5: Boolean = false,
/** 第 6 位0x40 */
var b6: Boolean = false,
/** 第 7 位最高位0x80 */
var b7: Boolean = false
) {
/**
* 通过一个 [Byte] 值构造 [ByteFlags] 实例
*
* @param byte 用于初始化的字节值仅使用低 8
*/
constructor(byte: Byte) : this() {
fromByte(byte)
}
/**
* 从给定的 [Byte] 值解析并设置各个位标志
*
* @param byte 要解析的字节
*/
fun fromByte(byte: Byte) {
val value = byte.toInt() and 0xFF
b0 = (value and 0x01) != 0
b1 = (value and 0x02) != 0
b2 = (value and 0x04) != 0
b3 = (value and 0x08) != 0
b4 = (value and 0x10) != 0
b5 = (value and 0x20) != 0
b6 = (value and 0x40) != 0
b7 = (value and 0x80) != 0
}
/**
* 将当前标志状态转换为一个 [Byte]
*
* @return 对应的字节值范围 0x00 ~ 0xFF以有符号字节形式返回
*/
fun toByte(): Byte {
var result = 0
if (b0) result = result or 0x01
if (b1) result = result or 0x02
if (b2) result = result or 0x04
if (b3) result = result or 0x08
if (b4) result = result or 0x10
if (b5) result = result or 0x20
if (b6) result = result or 0x40
if (b7) result = result or 0x80
return result.toByte()
}
/**
* 通过索引0~7获取对应位的布尔值
*
* @param index 位索引0 = LSB, 7 = MSB
* @return 该位是否为 true
* @throws IndexOutOfBoundsException 如果索引不在 0~7 范围内
*/
operator fun get(index: Int): Boolean {
return when (index) {
0 -> b0
1 -> b1
2 -> b2
3 -> b3
4 -> b4
5 -> b5
6 -> b6
7 -> b7
else -> throw IndexOutOfBoundsException("索引必须在0-7之间")
}
}
/**
* 通过索引0~7设置对应位的布尔值
*
* @param index 位索引0 = LSB, 7 = MSB
* @param value 要设置的布尔值
* @throws IndexOutOfBoundsException 如果索引不在 0~7 范围内
*/
operator fun set(index: Int, value: Boolean) {
when (index) {
0 -> b0 = value
1 -> b1 = value
2 -> b2 = value
3 -> b3 = value
4 -> b4 = value
5 -> b5 = value
6 -> b6 = value
7 -> b7 = value
else -> throw IndexOutOfBoundsException("索引必须在0-7之间")
}
}
/**
* 将所有 8 个位设置为 `true`
*/
fun setAll() {
b0 = true
b1 = true
b2 = true
b3 = true
b4 = true
b5 = true
b6 = true
b7 = true
}
/**
* 将所有 8 个位设置为 `false`
*/
fun clearAll() {
b0 = false
b1 = false
b2 = false
b3 = false
b4 = false
b5 = false
b6 = false
b7 = false
}
/**
* 对所有位执行逻辑取反true false
*/
fun toggleAll() {
b0 = !b0
b1 = !b1
b2 = !b2
b3 = !b3
b4 = !b4
b5 = !b5
b6 = !b6
b7 = !b7
}
/**
* 切换指定索引位的状态true false反之亦然
*
* @param index 要切换的位索引0~7
* @throws IndexOutOfBoundsException 如果索引无效
*/
fun toggle(index: Int) {
this[index] = !this[index]
}
/**
* 检查是否所有位都为 `true`
*
* @return 若全部为 true 则返回 true否则 false
*/
fun allTrue(): Boolean {
return b0 && b1 && b2 && b3 && b4 && b5 && b6 && b7
}
/**
* 检查是否所有位都为 `false`
*
* @return 若全部为 false 则返回 true否则 false
*/
fun allFalse(): Boolean {
return !b0 && !b1 && !b2 && !b3 && !b4 && !b5 && !b6 && !b7
}
/**
* 统计值为 `true` 的位数量
*
* @return true 位的个数0~8
*/
fun countTrue(): Int {
var count = 0
if (b0) count++
if (b1) count++
if (b2) count++
if (b3) count++
if (b4) count++
if (b5) count++
if (b6) count++
if (b7) count++
return count
}
/**
* 统计值为 `false` 的位数量
*
* @return false 位的个数0~8
*/
fun countFalse(): Int = 8 - countTrue()
/**
* 通过位名称 "b0""bit3" 获取对应位的值
*
* 支持别名`bN` `bitN`不区分大小写
*
* @param name 位的名称例如 "b0", "BIT5"
* @return 对应位的布尔值
* @throws IllegalArgumentException 如果名称无效
*/
fun getByName(name: String): Boolean {
return when (name.lowercase()) {
"b0", "bit0" -> b0
"b1", "bit1" -> b1
"b2", "bit2" -> b2
"b3", "bit3" -> b3
"b4", "bit4" -> b4
"b5", "bit5" -> b5
"b6", "bit6" -> b6
"b7", "bit7" -> b7
else -> throw IllegalArgumentException("未知的位名称: $name")
}
}
/**
* 将当前状态转换为标准二进制字符串MSB 在前
*
* 例如 b7=true, b6=false, ..., b0=true则返回 "10000001"
*
* @return 8 位二进制字符串高位在左
*/
fun toBinaryString(): String {
return buildString {
if (b7) append('1') else append('0')
if (b6) append('1') else append('0')
if (b5) append('1') else append('0')
if (b4) append('1') else append('0')
if (b3) append('1') else append('0')
if (b2) append('1') else append('0')
if (b1) append('1') else append('0')
if (b0) append('1') else append('0')
}
}
/**
* 将当前状态转换为十六进制字符串 0x 前缀大写两位
*
* 例如若值为 10则返回 "0x0A"
*
* @return 格式如 "0xXX" 的十六进制字符串
*/
fun toHexString(): String {
return "0x" + (toByte().toInt() and 0xFF).toString(16).padStart(2, '0').uppercase()
}
/**
* 将当前位状态转换为布尔数组
*
* 数组顺序为 [b0, b1, b2, ..., b7]LSB 在前
*
* @return 长度为 8 的布尔数组
*/
fun toBooleanArray(): BooleanArray {
return booleanArrayOf(b0, b1, b2, b3, b4, b5, b6, b7)
}
/**
* 从布尔数组初始化位状态
*
* 使用数组的前 8 个元素索引 0~7分别对应 b0~b7
*
* @param array 布尔数组长度至少为 8
* @throws IllegalArgumentException 如果数组长度不足 8
*/
fun fromBooleanArray(array: BooleanArray) {
require(array.size >= 8) { "数组长度至少为8" }
b0 = array[0]
b1 = array[1]
b2 = array[2]
b3 = array[3]
b4 = array[4]
b5 = array[5]
b6 = array[6]
b7 = array[7]
}
/**
* 对两个 [ByteFlags] 执行按位操作AND
*
* 结果的每一位 = this位 && other位
*
* @param other 另一个 ByteFlags 实例
* @return 新的 ByteFlags 实例表示 AND 结果
*/
infix fun and(other: ByteFlags): ByteFlags {
return ByteFlags(
this.b0 && other.b0,
this.b1 && other.b1,
this.b2 && other.b2,
this.b3 && other.b3,
this.b4 && other.b4,
this.b5 && other.b5,
this.b6 && other.b6,
this.b7 && other.b7
)
}
/**
* 对两个 [ByteFlags] 执行按位操作OR
*
* 结果的每一位 = this位 || other位
*
* @param other 另一个 ByteFlags 实例
* @return 新的 ByteFlags 实例表示 OR 结果
*/
infix fun or(other: ByteFlags): ByteFlags {
return ByteFlags(
this.b0 || other.b0,
this.b1 || other.b1,
this.b2 || other.b2,
this.b3 || other.b3,
this.b4 || other.b4,
this.b5 || other.b5,
this.b6 || other.b6,
this.b7 || other.b7
)
}
/**
* 对两个 [ByteFlags] 执行按位异或操作XOR
*
* 结果的每一位 = this位 != other位
*
* @param other 另一个 ByteFlags 实例
* @return 新的 ByteFlags 实例表示 XOR 结果
*/
infix fun xor(other: ByteFlags): ByteFlags {
return ByteFlags(
this.b0 != other.b0,
this.b1 != other.b1,
this.b2 != other.b2,
this.b3 != other.b3,
this.b4 != other.b4,
this.b5 != other.b5,
this.b6 != other.b6,
this.b7 != other.b7
)
}
/**
* 对当前 [ByteFlags] 执行按位操作NOT
*
* 每一位取反
*
* @return 新的 ByteFlags 实例所有位取反
*/
operator fun not(): ByteFlags {
return ByteFlags(
!b0, !b1, !b2, !b3, !b4, !b5, !b6, !b7
)
}
/**
* 返回可读的字符串表示包含二进制和十六进制形式
*
* 示例`ByteFlags(10100001 = 0xA1)`
*
* @return 字符串描述
*/
override fun toString(): String {
return "ByteFlags(${toBinaryString()} = ${toHexString()})"
}
companion object {
/**
* 创建一个所有位均为 `true` [ByteFlags] 实例
*
* @return 1 ByteFlags值为 0xFF
*/
fun allTrue(): ByteFlags {
return ByteFlags(true, true, true, true, true, true, true, true)
}
/**
* 创建一个所有位均为 `false` [ByteFlags] 实例
*
* @return 0 ByteFlags值为 0x00
*/
fun allFalse(): ByteFlags {
return ByteFlags()
}
/**
* 8 位二进制字符串创建 [ByteFlags] 实例
*
* 字符串格式高位在前MSB first "10000001" 表示 b7=1, b0=1
*
* @param binary 8 位二进制字符串仅含 '0' '1'
* @return 对应的 ByteFlags 实例
* @throws IllegalArgumentException 如果字符串长度不是 8 或包含非法字符
*/
fun fromBinaryString(binary: String): ByteFlags {
require(binary.length == 8) { "二进制字符串长度必须为8" }
require(binary.all { it == '0' || it == '1' }) { "二进制字符串只能包含0和1" }
return ByteFlags(
binary[7] == '1', // b0 是最低位(对应字符串最后一位)
binary[6] == '1',
binary[5] == '1',
binary[4] == '1',
binary[3] == '1',
binary[2] == '1',
binary[1] == '1',
binary[0] == '1' // b7 是最高位(对应字符串第一位)
)
}
/**
* 从整数创建 [ByteFlags] 实例仅使用低 8
*
* @param value 输入整数
* @return 对应的 ByteFlags 实例
*/
fun fromInt(value: Int): ByteFlags {
return ByteFlags((value and 0xFF).toByte())
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile Main.kt
* LastUpdate 2025-09-20 13:22:11
* LastUpdate 2026-01-06 14:36:10
* UpdateUser MingLiPro
*/
@file:JvmName("Main")
@ -24,12 +24,7 @@
package com.mingliqiye.utils.main
import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration
import com.mingliqiye.utils.stream.SuperStream
fun main() {
AutoConfiguration.printBanner()
val data = SuperStream.of(Array(0) { 1 })
println(data)
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile CallType.kt
* LastUpdate 2026-01-07 19:06:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatis
enum class CallType {
/**
* 通过索引访问结果集
* 使用数字索引位置来获取结果集中的数据
*/
RESULTSET_INDEX,
/**
* 通过名称访问结果集
* 使用列名来获取结果集中的数据
*/
RESULTSET_NAME,
/**
* 通过索引访问可调用语句
* 使用数字索引位置来获取可调用语句中的数据
*/
CALLABLE_STATEMENT_INDEX
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile QuickBaseTypeHandler.kt
* LastUpdate 2026-01-08 07:59:47
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatis
import org.apache.ibatis.type.BaseTypeHandler
import org.apache.ibatis.type.JdbcType
import java.sql.CallableStatement
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
/**
* 抽象类 QuickBaseTypeHandler MyBatis BaseTypeHandler 的扩展
* 提供了统一处理数据库字段与 Java 类型之间转换的抽象方法
* 子类需要实现 getValue setValue 方法来完成具体的类型转换逻辑
*
* @param T 要处理的 Java 类型
*/
abstract class QuickBaseTypeHandler<T> : BaseTypeHandler<T>() {
/**
* 抽象方法用于从数据库结果中获取并转换为 Java 类型 T
*
* @param vg 值获取器封装了 ResultSet CallableStatement
* @param ct 调用类型标识当前是从 ResultSet 还是 CallableStatement 获取数据
* @param ci 列索引可为 null
* @param cn 列名可为 null
* @return 转换后的 Java 类型 T 实例
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
abstract fun getValue(
vg: QuickBaseTypeHandlerValueGetter,
ct: CallType,
ci: Int?,
cn: String?
): T
/**
* 抽象方法用于将 Java 类型 T 设置到 PreparedStatement
*
* @param ps PreparedStatement 对象
* @param index 参数索引位置
* @param parameter Java 类型 T 的实例
* @param jdbcType JDBC 类型
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
abstract fun setValue(ps: PreparedStatement, index: Int, parameter: T, jdbcType: JdbcType?)
/**
* 实现 BaseTypeHandler setNonNullParameter 方法
* 将非空参数设置到 PreparedStatement
*
* @param ps PreparedStatement 对象
* @param i 参数索引位置
* @param parameter Java 类型 T 的实例非空
* @param jdbcType JDBC 类型
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
override fun setNonNullParameter(ps: PreparedStatement, i: Int, parameter: T, jdbcType: JdbcType?) {
setValue(ps, i, parameter, jdbcType)
}
/**
* 实现 BaseTypeHandler getNullableResult 方法
* 通过列名从 ResultSet 中获取可能为 null 的结果
*
* @param rs ResultSet 对象
* @param columnName 数据库列名
* @return 转换后的 Java 类型 T 实例可能为 null
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
override fun getNullableResult(rs: ResultSet, columnName: String): T {
return getValue(QuickBaseTypeHandlerValueGetter(null, rs), CallType.RESULTSET_NAME, null, columnName)
}
/**
* 实现 BaseTypeHandler getNullableResult 方法
* 通过列索引从 ResultSet 中获取可能为 null 的结果
*
* @param rs ResultSet 对象
* @param columnIndex 数据库列索引
* @return 转换后的 Java 类型 T 实例可能为 null
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
override fun getNullableResult(rs: ResultSet, columnIndex: Int): T {
return getValue(QuickBaseTypeHandlerValueGetter(null, rs), CallType.RESULTSET_INDEX, columnIndex, null)
}
/**
* 实现 BaseTypeHandler getNullableResult 方法
* 通过列索引从 CallableStatement 中获取可能为 null 的结果
*
* @param cs CallableStatement 对象
* @param columnIndex 数据库列索引
* @return 转换后的 Java 类型 T 实例可能为 null
* @throws SQLException SQL 执行异常时抛出
*/
@Throws(SQLException::class)
override fun getNullableResult(cs: CallableStatement, columnIndex: Int): T {
return getValue(QuickBaseTypeHandlerValueGetter(cs, null), CallType.CALLABLE_STATEMENT_INDEX, columnIndex, null)
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile QuickBaseTypeHandlerValueGetter.kt
* LastUpdate 2026-01-07 19:10:44
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatis
import java.sql.CallableStatement
import java.sql.ResultSet
data class QuickBaseTypeHandlerValueGetter(var callableStatement: CallableStatement?, var resultSet: ResultSet?)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,87 +16,48 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile DateTimeTypeHandler.kt
* LastUpdate 2025-09-15 13:53:53
* LastUpdate 2026-01-07 19:23:12
* UpdateUser MingLiPro
*/
@file:JvmName("DateTimeConvertor")
package com.mingliqiye.utils.mybatis.typehandler.datetime
import com.mingliqiye.utils.mybatis.CallType
import com.mingliqiye.utils.mybatis.QuickBaseTypeHandler
import com.mingliqiye.utils.mybatis.QuickBaseTypeHandlerValueGetter
import com.mingliqiye.utils.time.DateTime
import org.apache.ibatis.type.BaseTypeHandler
import org.apache.ibatis.type.JdbcType
import org.apache.ibatis.type.MappedTypes
import java.sql.CallableStatement
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
import java.time.LocalDateTime
/**
* 将LocalDateTime对象转换为DateTime对象
*
* @param localDateTime LocalDateTime对象
* @return DateTime对象如果localDateTime为null则返回null
*/
fun toDateTime(localDateTime: LocalDateTime?): DateTime? {
return localDateTime?.let { DateTime.of(it) }
}
/**
* 将DateTime对象转换为LocalDateTime对象
*
* @param dateTime DateTime对象
* @return LocalDateTime对象如果dateTime为null则返回null
*/
fun toLocalDateTime(dateTime: DateTime?): LocalDateTime? {
return dateTime?.toLocalDateTime()
}
import java.sql.Timestamp
/**
* DateTime类型处理器用于在数据库和Java对象之间转换DateTime类型
* @author MingLiQiye
* @date 2025/9/12
*/
@MappedTypes(DateTime::class)
class DateTimeTypeHandler : BaseTypeHandler<DateTime>() {
class DateTimeTypeHandler : QuickBaseTypeHandler<DateTime>() {
@Throws(SQLException::class)
override fun setNonNullParameter(
override fun getValue(
vg: QuickBaseTypeHandlerValueGetter,
ct: CallType,
ci: Int?,
cn: String?
): DateTime {
return DateTime.of(
(when (ct) {
CallType.RESULTSET_INDEX -> vg.resultSet!!.getTimestamp(ci!!)
CallType.RESULTSET_NAME -> vg.resultSet!!.getTimestamp(cn!!)
CallType.CALLABLE_STATEMENT_INDEX -> vg.callableStatement!!.getTimestamp(ci!!)
})
)
}
override fun setValue(
ps: PreparedStatement,
i: Int,
index: Int,
parameter: DateTime,
jdbcType: JdbcType?
) {
// 使用 setObject 允许传入 null由数据库处理
ps.setObject(i, toLocalDateTime(parameter))
}
@Throws(SQLException::class)
override fun getNullableResult(rs: ResultSet, columnName: String): DateTime? {
// 安全类型转换和空检查
return when (val value = rs.getObject(columnName)) {
is LocalDateTime -> toDateTime(value)
null -> null
else -> throw SQLException("Expected LocalDateTime for column '$columnName', but got ${value.javaClass.name}")
}
}
@Throws(SQLException::class)
override fun getNullableResult(rs: ResultSet, columnIndex: Int): DateTime? {
return when (val value = rs.getObject(columnIndex)) {
is LocalDateTime -> toDateTime(value)
null -> null
else -> throw SQLException("Expected LocalDateTime at column index $columnIndex, but got ${value.javaClass.name}")
}
}
@Throws(SQLException::class)
override fun getNullableResult(cs: CallableStatement, columnIndex: Int): DateTime? {
return when (val value = cs.getObject(columnIndex)) {
is LocalDateTime -> toDateTime(value)
null -> null
else -> throw SQLException("Expected LocalDateTime at column index $columnIndex, but got ${value.javaClass.name}")
}
ps.setTimestamp(index, Timestamp.valueOf(parameter.toLocalDateTime()))
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MysqlUUIDBinaryTypeHandler.kt
* LastUpdate 2026-01-07 19:30:31
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatis.typehandler.mysql.uuid
import com.mingliqiye.utils.mybatis.CallType
import com.mingliqiye.utils.mybatis.QuickBaseTypeHandler
import com.mingliqiye.utils.mybatis.QuickBaseTypeHandlerValueGetter
import com.mingliqiye.utils.uuid.UUID
import com.mingliqiye.utils.uuid.mysqlToUuid
import com.mingliqiye.utils.uuid.uuidToMysql
import org.apache.ibatis.type.JdbcType
import org.apache.ibatis.type.MappedTypes
import java.sql.PreparedStatement
@MappedTypes(UUID::class)
class MysqlUUIDBinaryTypeHandler : QuickBaseTypeHandler<UUID>() {
companion object {
/**
* 将字节数组转换为UUID对象
*
* @param byteArray 字节数组
* @return UUID对象如果字节数组为null则返回null
*/
private fun toUUID(byteArray: ByteArray?): UUID? {
return byteArray?.let { UUID.of(mysqlToUuid(it)) }
}
/**
* 将UUID对象转换为字节数组
*
* @param uuid UUID对象
* @return 字节数组如果UUID为null则返回null
*/
fun toByteArray(uuid: UUID?): ByteArray? {
return uuid?.let { uuidToMysql(it.toBytes()) }
}
}
override fun getValue(
vg: QuickBaseTypeHandlerValueGetter, ct: CallType, ci: Int?, cn: String?
): UUID {
return toUUID(
when (ct) {
CallType.RESULTSET_NAME -> vg.resultSet!!.getBytes(cn!!)
CallType.RESULTSET_INDEX -> vg.resultSet!!.getBytes(ci!!)
CallType.CALLABLE_STATEMENT_INDEX -> vg.resultSet!!.getBytes(ci!!)
}
)!!
}
override fun setValue(
ps: PreparedStatement, index: Int, parameter: UUID, jdbcType: JdbcType?
) {
ps.setBytes(index, toByteArray(parameter))
}
}

View File

@ -1,279 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUIDTypeHandler.kt
* LastUpdate 2025-09-15 13:54:18
* UpdateUser MingLiPro
*/
@file:JvmName("UUIDConvertor")
package com.mingliqiye.utils.mybatis.typehandler.uuid
import com.mingliqiye.utils.uuid.UUID
import org.apache.ibatis.type.BaseTypeHandler
import org.apache.ibatis.type.JdbcType
import org.apache.ibatis.type.MappedJdbcTypes
import org.apache.ibatis.type.MappedTypes
import java.sql.CallableStatement
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.util.UUID as JUUID
/**
* 将字节数组转换为UUID对象
*
* @param byteArray 字节数组
* @return UUID对象如果字节数组为null则返回null
*/
fun byteArraytoUUID(byteArray: ByteArray?): UUID? {
return byteArray?.let { UUID.of(it) }
}
/**
* 将UUID对象转换为字节数组
*
* @param uuid UUID对象
* @return 字节数组如果UUID为null则返回null
*/
fun uuidToByteArray(uuid: UUID?): ByteArray? {
return uuid?.toBytes()
}
/**
* 将字符串转换为UUID对象
*
* @param str 字符串
* @return UUID对象如果字符串为null则返回null
*/
fun stringToUUID(str: String?): UUID? {
return str?.let { UUID.of(it) }
}
/**
* 将UUID对象转换为字符串
*
* @param uuid UUID对象
* @return 字符串如果UUID为null则返回null
*/
fun uuidToString(uuid: UUID?): String? {
return uuid?.getString()
}
/**
* 将java UUID转换为UUID对象
*
* @param uuid JUUID java UUID
* @return UUID对象如果字符串为null则返回null
*/
fun juuidToUUID(uuid: JUUID?): UUID? {
return uuid?.let { UUID(it) }
}
/**
* 将UUID对象转换为java UUID
*
* @param uuid UUID对象
* @return java UUID如果UUID为null则返回null
*/
fun uuidToJuuid(uuid: UUID?): JUUID? {
return uuid?.getUuid()
}
/**
* JDBC类型转换 UUID BINARY
* @author MingLiQiye
* @date 2025/9/12
*/
@MappedTypes(UUID::class)
@MappedJdbcTypes(JdbcType.BINARY)
class UUIDBinaryTypeHandler : BaseTypeHandler<UUID>() {
/**
* 设置非空参数到PreparedStatement中
*
* @param ps PreparedStatement对象
* @param i 参数索引
* @param parameter UUID参数值
* @param jdbcType JDBC类型
*/
override fun setNonNullParameter(
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType?
) {
ps.setBytes(i, uuidToByteArray(parameter))
}
/**
* 从ResultSet中根据列名获取可空的UUID结果
*
* @param rs ResultSet对象
* @param columnName 列名
* @return UUID对象或null
*/
override fun getNullableResult(
rs: ResultSet, columnName: String
): UUID? {
return byteArraytoUUID(rs.getBytes(columnName))
}
/**
* 从ResultSet中根据列索引获取可空的UUID结果
*
* @param rs ResultSet对象
* @param columnIndex 列索引
* @return UUID对象或null
*/
override fun getNullableResult(rs: ResultSet, columnIndex: Int): UUID? {
return byteArraytoUUID(rs.getBytes(columnIndex))
}
/**
* 从CallableStatement中根据列索引获取可空的UUID结果
*
* @param cs CallableStatement对象
* @param columnIndex 列索引
* @return UUID对象或null
*/
override fun getNullableResult(
cs: CallableStatement, columnIndex: Int
): UUID? {
return byteArraytoUUID(cs.getBytes(columnIndex))
}
}
/**
* JDBC类型转换 UUID String
* @author MingLiQiye
* @date 2025/9/12
*/
@MappedTypes(UUID::class)
@MappedJdbcTypes(JdbcType.VARCHAR)
class UUIDStringTypeHandler : BaseTypeHandler<UUID>() {
/**
* 设置非空参数到PreparedStatement中
*
* @param ps PreparedStatement对象
* @param i 参数索引
* @param parameter UUID参数值
* @param jdbcType JDBC类型
*/
override fun setNonNullParameter(
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType?
) {
ps.setString(i, uuidToString(parameter))
}
/**
* 从ResultSet中根据列名获取可空的UUID结果
*
* @param rs ResultSet对象
* @param columnName 列名
* @return UUID对象或null
*/
override fun getNullableResult(
rs: ResultSet, columnName: String
): UUID? {
return stringToUUID(rs.getString(columnName))
}
/**
* 从ResultSet中根据列索引获取可空的UUID结果
*
* @param rs ResultSet对象
* @param columnIndex 列索引
* @return UUID对象或null
*/
override fun getNullableResult(rs: ResultSet, columnIndex: Int): UUID? {
return stringToUUID(rs.getString(columnIndex))
}
/**
* 从CallableStatement中根据列索引获取可空的UUID结果
*
* @param cs CallableStatement对象
* @param columnIndex 列索引
* @return UUID对象或null
*/
override fun getNullableResult(
cs: CallableStatement, columnIndex: Int
): UUID? {
return stringToUUID(cs.getString(columnIndex))
}
}
/**
* JDBC类型转换 UUID java UUID
* @author MingLiQiye
* @date 2025/9/12
*/
@MappedTypes(UUID::class)
@MappedJdbcTypes(JdbcType.OTHER)
class UUIDTypeHandler : BaseTypeHandler<UUID>() {
/**
* 设置非空参数到PreparedStatement中
*
* @param ps PreparedStatement对象
* @param i 参数索引
* @param parameter UUID参数值
* @param jdbcType JDBC类型
*/
override fun setNonNullParameter(
ps: PreparedStatement, i: Int, parameter: UUID, jdbcType: JdbcType?
) {
ps.setObject(i, parameter.getUuid())
}
/**
* 从ResultSet中根据列名获取可空的UUID结果
*
* @param rs ResultSet对象
* @param columnName 列名
* @return UUID对象或null
*/
override fun getNullableResult(
rs: ResultSet,
columnName: String
): UUID? {
return juuidToUUID(rs.getObject(columnName, JUUID::class.java) as JUUID)
}
/**
* 从ResultSet中根据列索引获取可空的UUID结果
*
* @param rs ResultSet对象
* @param columnIndex 列索引
* @return UUID对象或null
*/
override fun getNullableResult(rs: ResultSet, columnIndex: Int): UUID? {
return juuidToUUID(rs.getObject(columnIndex, JUUID::class.java) as JUUID)
}
/**
* 从CallableStatement中根据列索引获取可空的UUID结果
*
* @param cs CallableStatement对象
* @param columnIndex 列索引
* @return UUID对象或null
*/
override fun getNullableResult(
cs: CallableStatement, columnIndex: Int
): UUID? {
return juuidToUUID(cs.getObject(columnIndex, JUUID::class.java) as JUUID)
}
}

View File

@ -1,119 +0,0 @@
/*
* Copyright 2025 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MysqlUUIDBinaryTypeHandler.kt
* LastUpdate 2025-09-15 13:54:29
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.mybatis.typehandler.uuid.mysql
import com.mingliqiye.utils.uuid.UUID
import com.mingliqiye.utils.uuid.mysqlToUuid
import com.mingliqiye.utils.uuid.uuidToMysql
import org.apache.ibatis.type.BaseTypeHandler
import org.apache.ibatis.type.JdbcType
import org.apache.ibatis.type.MappedTypes
import java.sql.CallableStatement
import java.sql.PreparedStatement
import java.sql.ResultSet
/**
* JDBC类型转换 UUID
* @author MingLiQiye
* @date 2025/9/12
*/
@MappedTypes(UUID::class)
class MysqlUUIDBinaryTypeHandler : BaseTypeHandler<UUID>() {
/**
* 将字节数组转换为UUID对象
*
* @param byteArray 字节数组
* @return UUID对象如果字节数组为null则返回null
*/
private fun toUUID(byteArray: ByteArray?): UUID? {
return byteArray?.let { UUID.of(mysqlToUuid(it)) }
}
/**
* 将UUID对象转换为字节数组
*
* @param uuid UUID对象
* @return 字节数组如果UUID为null则返回null
*/
fun toByteArray(uuid: UUID?): ByteArray? {
return uuid?.let { uuidToMysql(it.toBytes()) }
}
/**
* 设置非空参数到PreparedStatement中
*
* @param ps PreparedStatement对象
* @param i 参数索引
* @param parameter UUID参数值
* @param jdbcType JDBC类型
*/
override fun setNonNullParameter(
ps: PreparedStatement,
i: Int,
parameter: UUID,
jdbcType: JdbcType?
) {
ps.setBytes(i, toByteArray(parameter))
}
/**
* 从ResultSet中根据列名获取可空的UUID结果
*
* @param rs ResultSet对象
* @param columnName 列名
* @return UUID对象或null
*/
override fun getNullableResult(
rs: ResultSet,
columnName: String
): UUID? {
return toUUID(rs.getBytes(columnName))
}
/**
* 从ResultSet中根据列索引获取可空的UUID结果
*
* @param rs ResultSet对象
* @param columnIndex 列索引
* @return UUID对象或null
*/
override fun getNullableResult(rs: ResultSet, columnIndex: Int): UUID? {
return toUUID(rs.getBytes(columnIndex))
}
/**
* 从CallableStatement中根据列索引获取可空的UUID结果
*
* @param cs CallableStatement对象
* @param columnIndex 列索引
* @return UUID对象或null
*/
override fun getNullableResult(
cs: CallableStatement,
columnIndex: Int
): UUID? {
return toUUID(cs.getBytes(columnIndex))
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile ClientScheduleReconnect.kt
* LastUpdate 2026-01-08 10:45:37
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.netty
import io.netty.bootstrap.Bootstrap
import io.netty.channel.Channel
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelFutureListener
import java.util.concurrent.TimeUnit
abstract class ClientScheduleReconnect(
protected val bootstrap: Bootstrap,
protected var delay: Long = 10L,
protected var timeUnit: TimeUnit = TimeUnit.SECONDS
) {
protected var isStop = false
protected var channel: Channel? = null
abstract fun onConnectedLog(channel: Channel)
abstract fun onConnectFailedLog(cause: Throwable?)
abstract fun onStoppedLog()
abstract fun doConnect(): ChannelFuture
open fun updateReconnectDelay(newDelay: Long, newTimeUnit: TimeUnit) {
require(newDelay > 0) { "Delay must be positive" }
this.delay = newDelay
this.timeUnit = newTimeUnit
}
open fun connect() {
doConnect().addListener(object : ChannelFutureListener {
override fun operationComplete(future: ChannelFuture) {
if (future.isSuccess) {
onConnected(future.channel())
} else {
onConnectFailed(future.cause())
}
}
})
}
open fun stop() {
isStop = true
disConnect()
}
open fun disConnect() {
channel?.close()
channel = null
}
open fun scheduleReconnect() {
if (isStop) {
onStoppedLog()
return
}
bootstrap.config().group().schedule({
if (isStop) {
onStoppedLog()
return@schedule
}
connect()
}, delay, timeUnit)
}
open fun onConnected(channel: Channel) {
this.channel = channel
onConnectedLog(channel)
channel.closeFuture().addListener { _ ->
scheduleReconnect()
}
}
open fun onConnectFailed(cause: Throwable?) {
onConnectFailedLog(cause)
scheduleReconnect()
}
open fun isConnected(): Boolean {
return channel?.isActive ?: false
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NamedThreadFactory.kt
* LastUpdate 2026-01-08 13:21:00
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.netty
import io.netty.util.concurrent.FastThreadLocalThread
import java.util.concurrent.ThreadFactory
import java.util.concurrent.atomic.AtomicInteger
open class NamedThreadFactory(private val getName: NamedThreadFactoryNameGetter) : ThreadFactory {
companion object {
@JvmStatic
private val allThreadPoolNumber = AtomicInteger(0)
@FunctionalInterface
fun interface NamedThreadFactoryNameGetter {
fun getName(clazz: Class<out NamedThreadFactory>, poolNumber: Int, threadNumber: Int): String
}
@JvmStatic
val defaultGetName =
NamedThreadFactoryNameGetter { clazz, poolNumber, threadNumber -> "${clazz.simpleName}-$poolNumber-$threadNumber" }
@JvmStatic
fun of(name: String): NamedThreadFactory {
return NamedThreadFactory { a, b, c ->
"$name-$c"
}
}
@JvmStatic
fun of(getter: NamedThreadFactoryNameGetter = defaultGetName): NamedThreadFactory {
return NamedThreadFactory(getter)
}
}
private val threadNumber = AtomicInteger(0)
private val threadPoolNumber = allThreadPoolNumber.addAndGet(1)
open fun getThreadName(clazz: Class<out NamedThreadFactory>, poolNumber: Int, threadNumber: Int) =
getName.getName(clazz, poolNumber, threadNumber)
override fun newThread(r: Runnable): Thread {
return FastThreadLocalThread(
null,
r,
getThreadName(this.javaClass, threadPoolNumber, threadNumber.addAndGet(1))
)
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile NettyUtils.kt
* LastUpdate 2026-01-07 10:00:31
* UpdateUser MingLiPro
*/
@file:JvmName("NettyUtils")
package com.mingliqiye.utils.netty
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import java.nio.ByteBuffer
/**
* 将ByteBuffer转换为Netty的ByteBuf对象
*
* 该函数使用Netty库的Unpooled工具类将标准的java.nio.ByteBuffer包装成Netty的ByteBuf
* 以便在Netty框架中使用
*
* @receiver ByteBuffer 需要转换的原始ByteBuffer对象
* @return ByteBuf 转换后的Netty ByteBuf对象该对象包装了原始的ByteBuffer
*/
fun ByteBuffer.toByteBuf(): ByteBuf {
return Unpooled.wrappedBuffer(this)
}
/**
* 将Netty的ByteBuf对象转换为ByteBuffer
*
* 该函数通过ByteBuf的nioBuffer()方法将Netty的ByteBuf转换为标准的java.nio.ByteBuffer
*
* @receiver ByteBuf 需要转换的原始ByteBuf对象
* @return ByteBuffer 转换后的java.nio.ByteBuffer对象
*/
fun ByteBuf.toByteBuffer(): ByteBuffer {
return this.nioBuffer()
}
/**
* 将Netty的ByteBuf对象转换为字节数组
*
* 该函数将ByteBuf中的可读字节读取到一个新的字节数组中
*
* @receiver ByteBuf 需要转换的原始ByteBuf对象
* @return ByteArray 转换后的字节数组包含ByteBuf中所有可读的字节数据
*/
fun ByteBuf.toByteArray(): ByteArray {
val array = ByteArray(this.readableBytes())
this.readBytes(array)
return array
}
/**
* 将字节数组转换为Netty的ByteBuf对象
*
* 该函数使用Netty库的Unpooled工具类将字节数组包装成Netty的ByteBuf
* 以便在Netty框架中使用
*
* @receiver ByteArray 需要转换的原始字节数组
* @return ByteBuf 转换后的Netty ByteBuf对象该对象包装了原始的字节数组
*/
fun ByteArray.toByteBuf(): ByteBuf {
return Unpooled.wrappedBuffer(this)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,16 +16,18 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile AddressPort.kt
* LastUpdate 2025-09-15 22:01:27
* LastUpdate 2026-01-06 14:03:47
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.network
import com.mingliqiye.utils.string.join
import java.io.Serializable
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.UnknownHostException
import java.nio.ByteBuffer
import java.util.regex.Pattern
/**
@ -80,6 +82,20 @@ class NetworkAddress private constructor(domip: String) : Serializable {
return NetworkAddress(domip)
}
@JvmStatic
fun ofIpv4(byteBuffer: ByteBuffer): NetworkAddress {
val byteArray = ByteArray(4)
byteBuffer.get(byteArray)
return ofIpv4(byteArray)
}
@JvmStatic
fun ofIpv4(byteArray: ByteArray): NetworkAddress {
return of(".".join(byteArray.map {
(it.toInt() and 0xFF).toString()
}))
}
/**
* 静态工厂方法通过 InetAddress 创建 NetworkAddress 实例
*
@ -182,6 +198,31 @@ class NetworkAddress private constructor(domip: String) : Serializable {
}
}
/**
* 将IPv4地址转换为字节数组
*
* @return 返回表示IPv4地址的4字节数组
* @throws NetworkException 当当前地址不是IPv4地址时抛出异常
*/
fun toIpv4ByteArray(): ByteArray {
// 验证地址类型是否为IPv4
if (iPv != IPV4) {
throw NetworkException("该地址 不是IPv4地址")
}
// 将IP地址字符串按点分割转换为整数并进行位运算处理最后转为字节数组
return ip!!.split(".").map { it.toInt() and 0xFF }.map { it.toByte() }.toByteArray()
}
/**
* 将IPv4地址写入到指定的ByteBuffer中
*
* @param byteBuffer 要写入的ByteBuffer对象IPv4地址将以字节数组形式写入到该缓冲区
* @return 返回写入了IPv4地址的ByteBuffer对象便于链式调用
*/
fun writeIpv4ToByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
return byteBuffer.put(toIpv4ByteArray())
}
/**
* 将当前 NetworkAddress 转换为 InetAddress 对象
*
@ -215,7 +256,54 @@ class NetworkPort : Serializable {
this.port = port
}
fun toByteArray(): ByteArray {
val byteArray = ByteArray(2)
byteArray[0] = (port shr 8 and 0xFF).toByte()
byteArray[1] = (port and 0xFF).toByte()
return byteArray
}
fun writeToByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
return byteBuffer.put(toByteArray())
}
companion object {
/**
* 创建NetworkPort实例
* @param port 端口号
* @return NetworkPort实例
*/
@JvmStatic
fun of(port: Int): NetworkPort {
return NetworkPort(port)
}
/**
* 从字节数组创建NetworkPort实例
* @param byteArray 包含端口信息的字节数组长度至少为2
* @return NetworkPort实例
*/
@JvmStatic
fun of(byteArray: ByteArray): NetworkPort {
// 将字节数组的前两个字节转换为端口号
val port = ((byteArray[0].toInt() and 0xFF) shl 8) or (byteArray[1].toInt() and 0xFF)
return of(port)
}
/**
* 从ByteBuffer创建NetworkPort实例
* @param byteBuffer 包含端口信息的ByteBuffer
* @return NetworkPort实例
*/
@JvmStatic
fun of(byteBuffer: ByteBuffer): NetworkPort {
val byteArray = ByteArray(2)
byteBuffer.get(byteArray)
return of(byteArray)
}
@JvmStatic
fun testPort(port: Int) {
// 验证端口号范围是否在0-65535之间
if (port !in 0..65535) {
@ -282,6 +370,42 @@ class NetworkEndpoint private constructor(
return NetworkEndpoint(networkAddress, networkPort)
}
/**
* 从字节数组创建IPv4网络端点
*
* @param byteArray 包含IPv4地址和端口信息的字节数组前4个字节表示IP地址后2个字节表示端口号
* @return NetworkEndpoint对象封装了IPv4地址和端口信息
*/
@JvmStatic
fun ofIpv4(byteArray: ByteArray): NetworkEndpoint {
// 提取前4个字节作为IP地址
val address = ByteArray(4) {
byteArray[it]
}
// 提取后2个字节作为端口号
val portInt = ByteArray(2) {
byteArray[it + 4]
}
return NetworkEndpoint(NetworkAddress.ofIpv4(address), NetworkPort.of(portInt))
}
/**
* 从字节缓冲区创建IPv4网络端点实例
*
* 该方法从给定的ByteBuffer中读取数据构建一个包含IPv4地址和端口的网络端点对象
* 该方法按照协议顺序从缓冲区中读取IPv4地址数据和端口数据
*
* @param byteBuffer 包含IPv4地址和端口数据的字节缓冲区缓冲区中的数据应按照先地址后端口的顺序排列
* @return 返回一个NetworkEndpoint实例包含从缓冲区解析出的IPv4地址和端口信息
*/
@JvmStatic
fun ofIpv4(byteBuffer: ByteBuffer): NetworkEndpoint {
return NetworkEndpoint(
NetworkAddress.ofIpv4(byteBuffer),
NetworkPort.of(byteBuffer)
)
}
/**
* 根据"host:port"格式的字符串创建NetworkEndpoint实例
* 例如"127.0.0.1:8080"
@ -327,7 +451,7 @@ class NetworkEndpoint private constructor(
* @return 包含详细信息的字符串
*/
override fun toString(): String {
return "NetworkEndpoint(IP=${networkAddress.ip},Port=${networkPort.port},Endpoint=${toHostPortString()})"
return "NetworkEndpoint(IP=${networkAddress.ip},Port=${networkPort.port})"
}
/**
@ -347,4 +471,39 @@ class NetworkEndpoint private constructor(
fun port(): Int {
return networkPort.port
}
/**
* 将网络地址和端口转换为IPv4字节数组
*
* 该函数将当前对象的网络地址转换为IPv4字节数组并与端口字节数组合并
* 生成一个包含6个字节的数组其中前4个字节为IPv4地址后2个字节为端口号
*
* @return ByteArray 包含6个字节的数组前4个字节为IPv4地址后2个字节为端口号
*/
fun toIpv4ByteArray(): ByteArray {
val ipv4ByteArray = networkAddress.toIpv4ByteArray()
val portByteArray = networkPort.toByteArray()
// 构建包含IPv4地址和端口的6字节数组
val byteArray = ByteArray(6) {
if (it < 4) {
ipv4ByteArray[it]
} else {
portByteArray[it - 4]
}
}
return byteArray
}
/**
* 将IPv4地址和端口信息写入字节缓冲区
*
* 该函数依次将网络地址的IPv4表示和网络端口写入到指定的字节缓冲区中
*
* @param byteBuffer 要写入数据的目标字节缓冲区
*/
fun writeIpv4toByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
networkAddress.writeIpv4ToByteBuffer(byteBuffer)
networkPort.writeToByteBuffer(byteBuffer)
return byteBuffer
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,20 +15,15 @@
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile StreamEmptyException.java
* LastUpdate 2025-09-20 13:24:07
* CurrentFile StreamEmptyException.kt
* LastUpdate 2026-01-07 19:13:29
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.stream;
package com.mingliqiye.utils.stream
public class StreamEmptyException extends java.lang.RuntimeException {
class StreamEmptyException : RuntimeException {
constructor(message: String) : super(message)
public StreamEmptyException(String message) {
super(message);
}
public StreamEmptyException(String message, Throwable cause) {
super(message, cause);
}
constructor(message: String, cause: Throwable) : super(message, cause)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,10 +16,10 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile MysqlUUIDv1.kt
* LastUpdate 2025-09-15 22:32:50
* LastUpdate 2026-01-08 08:22:14
* UpdateUser MingLiPro
*/
@file:JvmName("MysqlUUIDv1")
@file:JvmName("MysqlUUIDConvertor")
package com.mingliqiye.utils.uuid

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 mingliqiye
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUID.kt
* LastUpdate 2025-09-19 20:22:27
* LastUpdate 2026-01-08 13:21:00
* UpdateUser MingLiPro
*/
@ -234,6 +234,19 @@ class UUID : Serializable {
return UUID(msb, lsb)
}
/**
* 从ByteBuffer创建UUID对象
*
* @param byteBuffer 包含UUID字节数据的ByteBuffer对象必须包含至少16个字节的数据
* @return 从ByteBuffer中读取的16个字节创建的UUID对象
*/
@JvmStatic
fun of(byteBuffer: ByteBuffer): UUID {
val byte = ByteArray(16)
byteBuffer.get(byte)
return UUID(byte)
}
/**
* 根据 MySQL UUID 字节数组创建 UUID 实例
*
@ -296,7 +309,6 @@ class UUID : Serializable {
return UUID(this)
}
/**
* 从字符串解析 UUID 字节数组
*
@ -422,6 +434,10 @@ class UUID : Serializable {
return data
}
fun writeToByteBuffer(byteBuffer: ByteBuffer): ByteBuffer {
return byteBuffer.put(data)
}
/**
* 返回 UUID 的高位长整型部分
*
@ -521,7 +537,7 @@ class UUID : Serializable {
* @return MySQL 格式的 UUID 字节数组
*/
fun toMysql(): ByteArray {
return mysqlToUuid(this.data)
return uuidToMysql(this.data)
}
/**
@ -530,7 +546,7 @@ class UUID : Serializable {
* @return MySQL 格式的 UUID 实例
*/
fun toMysqlUUID(): UUID {
return of(mysqlToUuid(this.data))
return of(uuidToMysql(this.data))
}
/**
@ -588,6 +604,15 @@ class UUID : Serializable {
* @return 包含 UUID 和版本号的字符串
*/
override fun toString(): String {
when (version) {
1 -> {
return "UUID(uuid=${getString()},version=${version},datetime=${getDateTime()},mac=${getMac()})"
}
6, 7 -> {
return "UUID(uuid=${getString()},version=${version},datetime=${getDateTime()})"
}
}
return "UUID(uuid=${getString()},version=${version})"
}