尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
SQLDelight JVM 版 H2/HSQL 方言:列类型映射、自定义列类型与乐观锁实战指南
后端ORM【免费下载链接】sqldelightSQLDelight - Generates typesafe Kotlin APIs from SQL项目地址https://gitcode.com/gh_mirrors/sq/sqldelight点击查看免费下载SQLDelight 会根据你编写的 SQL 表结构自动生成类型安全的 Kotlin 数据类与查询接口而在 JVM 上使用 H2/HSQL 方言时数据库列类型到 Kotlin 类型的映射规则、自定义列类型ColumnAdapter、值类型VALUE与乐观锁LOCK是保证代码生成结果正确性的关键。本文以 docs/jvm_h2/types.md 为核心骨架结合本仓库dialects/hsql方言模块与runtime运行时源码完整讲解 H2 列类型映射表、自定义列类型、枚举适配、值类型与乐观锁的声明方式、配置示例及底层实现原理读完即可在项目中准确声明表结构并处理复杂列类型。H2/HSQL 列类型与 Kotlin 类型映射表SQLDelight 中的列定义与标准 H2 列定义完全一致唯一额外支持的是一个列约束extra column constraint用于指定该列在生成的接口中的 Kotlin 类型其语法见下文 自定义列类型 一节。下面这张完整的映射表来自 docs/jvm_h2/types.md它覆盖了 H2/HSQL 方言下几乎所有常见 SQL 类型的默认 Kotlin 映射CREATE TABLE some_types ( some_tiny_int TINYINT, -- Retrieved as Byte some_small_int SMALLINT, -- Retrieved as Short some_integer INTEGER, -- Retrieved as Int some_int INT, -- Retrieved as Int some_big_int BIGINT, -- Retrieved as Long some_decimal DECIMAL(6,5), -- Retrieved as Int some_dec DEC(6,5), -- Retrieved as Int some_numeric NUMERIC(6,5), -- Retrieved as Int some_float FLOAT(6), -- Retrieved as Double some_real REAL, -- Retrieved as Double some_double DOUBLE, -- Retrieved as Double some_double_precision DOUBLE PRECISION, -- Retrieved as Double some_boolean BOOLEAN, -- Retrieved as Boolean some_date DATE, -- Retrieved as String some_time TIME, -- Retrieved as String some_timestamp2 TIMESTAMP(6), -- Retrieved as String some_char CHAR, -- Retrieved as String some_character CHARACTER(6), -- Retrieved as String some_char_varying CHAR VARYING(6), -- Retrieved as String some_longvarchar LONGVARCHAR, -- Retrieved as String some_character_varying CHARACTER VARYING(6), -- Retrieved as String some_varchar VARCHAR(16), -- Retrieved as String some_clo CHARACTER LARGE OBJECT(16), -- Retrieved as String some_clob clob(16 M CHARACTERS), -- Retrieved as String some_binary BINARY, -- Retrieved as ByteArray some_binary2 BINARY(6), -- Retrieved as ByteArray some_longvarbinary LONGVARBINARY, -- Retrieved as ByteArray some_longvarbinary2 LONGVARBINARY(6), -- Retrieved as ByteArray some_binary_varying BINARY VARYING(6), -- Retrieved as ByteArray some_varbinary VARBINARY(8), -- Retrieved as ByteArray some_uuid UUID, -- Retrieved as ByteArray some_blob BLOB, -- Retrieved as ByteArray some_blo BINARY LARGE OBJECT(6), -- Retrieved as ByteArray some_bit BIT, -- Retrieved as ByteArray some_bit2 BIT(6), -- Retrieved as ByteArray some_bit_varying BIT VARYING(6), -- Retrieved as ByteArray some_interval INTERVAL YEAR TO MONTH, -- Retrieved as ByteArray some_interval2 INTERVAL YEAR(3), -- Retrieved as ByteArray some_interval3 INTERVAL DAY(4) TO HOUR, -- Retrieved as ByteArray some_interval4 INTERVAL MINUTE(4) TO SECOND(6), -- Retrieved as ByteArray some_interval5 INTERVAL SECOND(4,6) -- Retrieved as ByteArray );将上述内容整理为速查表便于在声明表结构时快速对照H2/HSQL SQL 类型生成的 Kotlin 类型说明TINYINTByte8 位有符号整数SMALLINTShort16 位有符号整数INTEGER/INTInt32 位有符号整数BIGINTLong64 位有符号整数DECIMAL(p,s)/DEC(p,s)/NUMERIC(p,s)Int定点数fixed-point映射为整数FLOAT(p)/REAL/DOUBLE/DOUBLE PRECISIONDouble近似浮点数approximate numericBOOLEANBoolean布尔值DATE/TIME/TIMESTAMP(p)String日期、时间、时间戳以字符串形式取出CHAR/CHARACTER(n)/CHAR VARYING(n)/VARCHAR(n)/LONGVARCHAR/CHARACTER VARYING(n)/CHARACTER LARGE OBJECT(n)/CLOBString字符字符串character string与大型字符对象BINARY/BINARY(n)/LONGVARBINARY/BINARY VARYING(n)/VARBINARY(n)/UUID/BLOB/BINARY LARGE OBJECT(n)/BIT/BIT(n)/BIT VARYING(n)ByteArray二进制字符串、位串、UUID 与大型二进制对象INTERVAL如YEAR TO MONTH、DAY TO HOUR、MINUTE TO SECOND等ByteArray时间段类型需要特别说明两点原文档标题为 “MySQL Types”但从内容看它列出的TINYINT、LONGVARCHAR、INTERVAL等均为 H2/HSQL 的列类型且文档开头明确写道 “SQLDelight column definitions are identical to regular H2 column definitions”因此该小节实际描述的是 H2 方言的类型映射属于文档标题的历史遗留问题阅读时以正文内容为准。表中DECIMAL/NUMERIC等定点数默认映射为Int如果精度超出整数范围应配合下文的自定义列类型将其映射为更合适的 Kotlin 类型。类型映射在方言模块中的实现上述映射并非硬编码在文档里而是由本仓库dialects/hsql方言模块中的类型解析器驱动。查看 HsqlTypeResolver.kt 可以看到definitionType根据 PSI 语法树中的类型节点分类返回IntermediateTypeapproximateNumericDataTypeFLOAT/REAL/DOUBLE等→PrimitiveType.REAL对应 KotlinDoublebinaryStringDataType与bitStringDataType→PrimitiveType.BLOB对应ByteArraydateDataType→PrimitiveType.TEXT对应StringfixedPointDataTypeDECIMAL/NUMERIC→PrimitiveType.INTEGERcharacterStringDataType→PrimitiveType.TEXTintervalDataType→PrimitiveType.BLOB其余类型节点则落到方言自定义的 HsqlType.kt 枚举TINY_INT→Byte、SMALL_INT→Short、INTEGER→Int、BIG_INT→Long、BOOL→Boolean。其中BOOL的读写实现也很有代表性decode将驱动返回的1L转换为truevalue 1Lencode将Boolean写回1L/0L且所有整数类列含BOOL在 JDBC 绑定与游标读取时统一走bindLong/getLong见 HsqlType.kt。理解这一点有助于排查“为什么 TINYINT 在驱动层是 Long 而生成的 Kotlin 类型是 Byte”之类的疑问——转换由生成的代码完成。此外HsqlTypeResolver还处理了COALESCE/IFNULL/GREATEST/LEAST/MAX/MIN等函数的返回类型推导以及length等字符串函数返回BIG_INTLong的规则见 HsqlTypeResolver.kt。自定义列类型Custom Column Types内置映射只覆盖“SQL 类型 → 基础 Kotlin 类型”这一层。如果希望把列读成更贴合业务的自定义类型可以在列定义时通过AS Kotlin 类型显式指定这一语法正是文档开头提到的“额外列约束”。该小节内容与仓库共享文档 docs/common/custom_column_types.md 一致被 H2、SQLite、MySQL 等多个方言文档共同引用。以“把逗号分隔的字符串列读成ListString”为例import kotlin.String; import kotlin.collections.List; CREATE TABLE hockeyPlayer ( cup_wins TEXT AS ListString NOT NULL );注意.sq文件顶部需要用import语句引入用到的 Kotlin 类型含包名这样生成的代码才能正确引用。声明了自定义类型后创建Database时必须提供一个ColumnAdapter负责在数据库类型与自定义类型之间做双向映射val listOfStringsAdapter object : ColumnAdapterListString, String { override fun decode(databaseValue: String) if (databaseValue.isEmpty()) { listOf() } else { databaseValue.split(,) } override fun encode(value: ListString) value.joinToString(separator ,) } val queryWrapper: Database Database( driver driver, hockeyPlayerAdapter hockeyPlayer.Adapter( cup_winsAdapter listOfStringsAdapter ) )ColumnAdapter的接口定义位于运行时 ColumnAdapter.kt它是一个双泛型接口ColumnAdapterT : Any, S其中S必须是数据库侧支持的类型之一Long、Double、String、ByteArraydecode负责把数据库值S解码成业务类型Tencode负责把T编码回S。生成代码在查询时调用decode在写入/更新时调用encode从而把“类型转换”集中收敛到这一个适配器里。枚举列内置的 EnumColumnAdapter作为便利设施SQLDelight 运行时自带一个把枚举以字符串形式存储的ColumnAdapter无需手写样板代码。先在 SQL 中把列声明为某个枚举类型import com.example.hockey.HockeyPlayer; CREATE TABLE hockeyPlayer ( position TEXT AS HockeyPlayer.Position )构造Database时传入运行时提供的EnumColumnAdapter()val queryWrapper: Database Database( driver driver, hockeyPlayerAdapter HockeyPlayer.Adapter( positionAdapter EnumColumnAdapter() ) )EnumColumnAdapter的实现位于 EnumColumnAdapter.ktdecode按枚举名name从enumValues中查找对应枚举常量encode直接返回value.name即数据库里存的是枚举常量名。它通过inline fun reified T : EnumT EnumColumnAdapter()工厂函数配合enumValues()自动获取枚举常量数组所以调用处无需传任何参数。值类型Value Types除了自定义类型SQLDelight 还支持为列生成一个值类型value type——一个包装底层数据库类型的 Kotlin 类型。声明方式是在列上追加AS VALUECREATE TABLE hockeyPlayer ( id INT AS VALUE );从编译器源码看这一约束由 ColumnTypeMixin.kt 识别当列类型节点的子节点中出现VALUE或LOCK关键字时会为对应列生成带VALUE修饰符的 Kotlin 包装类型。值类型的引入让列不再是裸的Int/String而是有明确语义的领域类型例如后续章节的乐观锁列就是值类型的一个典型应用。乐观锁Optimistic Locking值类型之上SQLDelight 提供了一个开箱即用的并发控制机制把某一列声明为LOCK编译器会为该列生成值类型强制所有UPDATE语句必须正确使用该锁进行更新否则编译报错。声明方式与约束示例如下与共享文档 docs/common/types_server_migrations.md 一致CREATE TABLE hockeyPlayer( id INT AS VALUE, version_number INT AS LOCK, name VARCHAR(8) ); -- This will fail (and the IDE plugin will suggest rewriting to the below) updateName: UPDATE hockeyPlayer SET name ?; -- This will pass compilation updateNamePassing: UPDATE hockeyPlayer SET name ? version_number :version_number 1 WHERE version_number :version_number;其背后的校验逻辑实现在编译器的 OptimisticLockValidator.kt 中规则可以归纳为三条SET 子句必须包含锁列更新语句的SET中必须出现锁列否则报 “This statement is missing the optimistic lock in its SET clause.”L69-L80锁必须自增 1SET中锁列的表达必须严格形如lock :lock 1绑定参数自增或lock lock 1列自增否则报 “The optimistic lock must be set exactly like ...”L91-L103列自增selfIncrements时无需再校验 WHERE 子句WHERE 子句必须校验锁对于绑定参数自增的形式还必须满足WHERE lock :lock形式的相等比较否则报 “The optimistic lock must be queried exactly like ...”L113-L143。也就是说updateNamePassing这种写法把“读取版本号 → 版本号 1 写回 → 按旧版本号过滤影响行数”的乐观锁流程固化进了编译期约束任何绕过锁的UPDATE都无法通过编译。同时编译器的QueryGenerator也会识别LOCK列并参与查询代码生成见 QueryGenerator.kt而 IDE 插件会在违反约束时提供快速修复建议quickFix见 OptimisticLockValidator.kt。自定义类型与迁移Migrations当迁移文件.sqm作为 schema 的权威来源时同样可以在ALTER TABLE中为新增列指定暴露给 Kotlin 的列类型import kotlin.String; import kotlin.collection.List; ALTER TABLE my_table ADD COLUMN new_column VARCHAR(8) AS ListString;注意示例中导入的是kotlin.collection.List原文档写法实际使用时请导入正确的包名kotlin.collections.List。该语法与建表语句中的AS Kotlin 类型完全一致因此迁移后新增的列也会在生成的数据类/查询接口中呈现为自定义类型并且同样需要为Database提供对应的ColumnAdapter。小结与使用建议默认映射优先H2/HSQL 下绝大多数列都可以直接命中 docs/jvm_h2/types.md 中的默认映射表无需额外配置业务语义列使用自定义类型需要把列读成List、枚举、自定义数据类时用AS Kotlin 类型ColumnAdapter组合转换逻辑集中在适配器内生成代码保持简洁枚举优先用EnumColumnAdapter()运行时内置实现已覆盖“枚举 ↔ 字符串”这一最常见场景并发更新使用LOCK乐观锁声明LOCK列后编译器与 IDE 会在编译期强制校验UPDATE的写法将乐观锁约定固化为代码约束迁移同样支持自定义类型schema 以迁移为权威时ALTER TABLE ADD COLUMN ... AS ...同样生效。以上类型声明、适配器实现与校验规则均可在本仓库对应源码中验证方言映射见 HsqlTypeResolver.kt 与 HsqlType.kt运行时适配器见 ColumnAdapter.kt 与 EnumColumnAdapter.kt乐观锁校验见 OptimisticLockValidator.kt。共享文档 docs/common/custom_column_types.md 与 docs/common/types_server_migrations.md 还给出了跨方言通用的写法可供迁移到其他方言时对照参考。赞分享后端ORM【免费下载链接】sqldelightSQLDelight - Generates typesafe Kotlin APIs from SQL项目地址https://gitcode.com/gh_mirrors/sq/sqldelight点击查看免费下载相关推荐Kubernetes Goat 场景 4 实战特权容器逃逸至宿主机并夺取节点级凭据Kubernetes Goat 场景 4 实战特权容器逃逸至宿主机并夺取节点级凭据 本篇基于 Kubernetes Goat 场景 4Container后端ORMSQLDelight PostgreSQL 类型映射实战从 SQL 列类型到 Kotlin 类型系统SQLDelight PostgreSQL 类型映射实战从 SQL 列类型到 Kotlin 类型系统 本篇技术指南以 docs/jvm_postgresql/后端ORMSQLDelight MySQL 类型映射全指南从 SQL 列类型到 Kotlin 类型的自动转换与自定义适配SQLDelight MySQL 类型映射全指南从 SQL 列类型到 Kotlin 类型的自动转换与自定义适配 导读 本文聚焦 SQLDelight 在 JV后端ORM上一篇ScrollableLayout最佳实践解决Android开发中的滚动冲突问题下一篇Get Shit Done进阶技巧自定义工作流代理提升开发效率创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

CloudQuery BigQuery 目的地插件测试覆盖率报告全解读:coverage.md 生成机制与 13.7% 背后的测试策略

CloudQuery BigQuery 目的地插件测试覆盖率报告全解读:coverage.md 生成机制与 13.7% 背后的测试策略

数据集成数据工程数据分析 【免费下载链接】cloudquery Data pipelines for cloud config and security data. Build cloud asset inventory, CSPM, FinOps, and vulnerability management solutions. Extract from AWS, Azure, GCP, and 70 cloud and SaaS sources. 项目地址&…

📅 2026/10/8 13:57:43
题解:洛谷 P2730 [USACO3.2] 魔板 Magic Squares

题解:洛谷 P2730 [USACO3.2] 魔板 Magic Squares

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大家订阅我的专栏:算法…

📅 2026/10/8 13:57:43
4800张真实废弃物图像分类:从数据清洗到迁移学习全流程实战

4800张真实废弃物图像分类:从数据清洗到迁移学习全流程实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/8 13:52:42
MORE NEWS

更多资讯

📰

三十岁运维转行网安:十个月学习路线与实战经验分享

三十岁那年的春节,我人在机房,窗外烟花正浓,面前是密密麻麻的告警。那一夜我处理了三起故障:一台数据库服务器磁盘写满,一套业务系统进程假死,还有一个开发环境因为某些兼容问题起不来。每一步操作都和五年…

📰

Linux软件包与进程管理实战:从命令到排错全攻略

刚接触 Linux 的人最容易陷入一个误区:抱着命令手册背了一堆ls、cd、cp,结果真到了使用场景,装软件装不上,程序跑起来卡死,想关关不掉,最后只能重启机器。我在带新人或者帮朋友排查服务器问题时&#xff0c…

📰

JavaWeb门诊系统源码实战:Servlet+JSP+MySQL完整业务闭环

简介:本资源是一套完整的基于JavaWeb开发的医院门诊病人管理系统源码,面向计算机专业本科生毕业设计、课程设计及JavaWeb初学者实践学习,聚焦挂号、就诊、缴费、取药等核心医疗业务流程的信息化实现。压缩包共831个文件,含440个HT…

📰

ATR2660 LNA芯片深度解析:GNSS射频前端设计核心指南

1. 这颗芯片不是“拿来即用”的玩具,而是北斗/GPS信号链里真正扛压的“第一道关卡”ATR2660——这个名字在卫星导航硬件工程师的日常对话里,往往不是出现在采购清单最前面,而是藏在射频前端设计文档的第一页脚注里。它不是STM32那种靠Keil5点…

📰

SpringBoot对接钉钉机器人:从加签到消息推送的完整实战指南

SpringBoot对接钉钉机器人这件事,我在项目里已经反复折腾过好几轮了。最开始只是想让系统异常的时候能第一时间提醒我,结果越做越深入,从最简单的文本消息到markdown卡片、定时日报、异步告警,基本上把钉钉自定义机器人的玩法都摸…

📰

Git 别名配置完全指南:从基础到高级,打造高效终端操作

1. 为什么需要 Git 别名?先从“手很累”说起 我刚开始用 Git 的头半年,一直没搞明白为什么周围的老程序员敲命令那么快。后来偷偷看他们的终端历史记录,发现每个人的命令都短得离谱,比如 git st 、 git br 、 git lg &#…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬