SQLDelight 类型系统实战指南:SQLite 类型映射、自定义列类型(ColumnAdapter)与 Value 类型
后端ORM【免费下载链接】sqldelightSQLDelight - Generates typesafe Kotlin APIs from SQL项目地址https://gitcode.com/gh_mirrors/sq/sqldelight点击查看免费下载导读本文以 SQLDelight 官方文档中的 SQLite 类型章节为核心系统讲解 SQLDelight 中从 SQL 类型到 Kotlin 类型的映射规则、AS扩展列约束语法、原始类型适配器primitive-adapters、自定义列类型ColumnAdapter、枚举存储与 Value 类型包装。读完本文你将掌握如何为任意 SQLite 列声明所需的 Kotlin 类型并能在创建Database时为自定义类型注入编解码适配器从而让生成的数据访问层直接暴露类型安全的领域对象。本文内容同时适用于 SQLDelight 的 JS/SQLiteWebAssembly、sql.js以及各 JVM 方言环境核心语法与 API 完全一致。SQLite 默认类型映射SQLDelight 的列定义与标准 SQLite 列定义完全一致唯一的差别是你可以通过一个**额外的列约束extra column constraint**为列指定生成接口中所使用的 Kotlin 类型这就是本文后续要重点讲解的AS子句。若不使用该扩展SQLDelight 会按照 SQLite 的类型亲和性type affinity采用以下默认映射CREATE TABLE some_types ( some_long INTEGER, -- 数据库中存为 INTEGER读取为 Long some_double REAL, -- 数据库中存为 REAL读取为 Double some_string TEXT, -- 数据库中存为 TEXT读取为 String some_blob BLOB -- 数据库中存为 BLOB读取为 ByteArray );对应的类型对照关系如下SQLite 存储类型SQLDelight 生成的 Kotlin 类型说明INTEGERLong有符号 64 位整数REALDoubleIEEE 754 浮点数TEXTStringUTF-8/UTF-16 文本BLOBByteArray二进制字节数组这也是ColumnAdapter接口中数据库侧类型仅限四种的原因运行时把Long、Double、String、ByteArray视为 SQLite 的四种基础存储类型见 ColumnAdapter.kt 的接口注释。当某一列的 Kotlin 类型与上述四种默认类型不一致时就需要适配器在这两者之间做转换。扩展列约束的语法规则AS子句并不是魔法它由 SQLDelight 的 ANTLR 语法文件明确定义。在 sqldelight.bnf 中列类型的产生式如下column_type :: {type_name} [ AS ( VALUE | LOCK | ( annotation) * java_type_name ) ]也就是说任何合法 SQLite 类型名type_name后面都可以追加AS扩展其后允许出现三种形态VALUE让 SQLDelight 为该列生成一个包装底层数据库类型的 Value 类型见下文LOCK锁定列的值类型配合 value 类型使用一个或多个注解之后跟随的 Java/Kotlin 类型名java_type_name支持泛型参数例如AS ListString、AS HockeyPlayer.Position。类型名解析规则同样定义在 sqldelight.bnf支持嵌套泛型如ListListString。这意味着AS语法本身由编译器负责解析而数据库表结构中并不存在AS列——它纯粹是 SQLDelight 的编译期扩展用来告诉代码生成器该列在 Kotlin 侧应该是什么类型。原始类型适配器primitive-adaptersSQLite 底层只有INTEGER、REAL、TEXT、BLOB四种存储类型因此Long、Double、String、ByteArray之外的其他 Kotlin 原始类型如Int、Float、Short无法直接映射。为此 SQLDelight 提供了独立的primitive-adapters模块其中预置了三个开箱即用的适配器。添加依赖 Kotlin DSLkotlin dependencies { implementation(app.cash.sqldelight:primitive-adapters:{{ versions.sqldelight }}) } Groovy DSLgroovy dependencies { implementation app.cash.sqldelight:primitive-adapters:{{ versions.sqldelight }} }{{ versions.sqldelight }}是文档中的版本占位符实际使用时请替换为你所采用的 SQLDelight 版本号。预置的三种适配器模块提供以下适配器源码位于 adapters/primitive-adaptersFloatColumnAdapter— 将隐式以Double存储的 SQL 类型读取为kotlin.FloatIntColumnAdapter— 将隐式以Long存储的 SQL 类型读取为kotlin.IntShortColumnAdapter— 将隐式以Long存储的 SQL 类型读取为kotlin.Short。以IntColumnAdapter为例它的实现就是一个纯粹的Long与Int互转的ColumnAdapter// adapters/primitive-adapters/src/commonMain/kotlin/app/cash/sqldelight/adapter/primitive/IntColumnAdapter.kt object IntColumnAdapter : ColumnAdapterInt, Long { override fun decode(databaseValue: Long): Int databaseValue.toInt() override fun encode(value: Int): Long value.toLong() }该模块的每个适配器都带有对应的跨平台单元测试如 IntColumnAdapterTest.kt可验证其编解码行为。由于这些适配器以object单例实现它们无状态、可复用适合在多处重复注入。使用方式与自定义适配器一致在创建Database时把对应列的 adapter 传入生成表的Adapter构造器即可见下一节。自定义列类型Custom Column Types当预置适配器无法满足需求时你可以让任意一列以自定义 Kotlin 类型出现。做法分两步先在.sq文件中用AS声明目标类型再在创建Database时提供对应的ColumnAdapter。第一步在 SQL 中声明 Kotlin 类型import kotlin.String; import kotlin.collections.List; CREATE TABLE hockeyPlayer ( cup_wins TEXT AS ListString NOT NULL );注意 SQL 文件顶部的import语句自定义类型必须是可解析的完整类型名泛型类型如ListString同样支持。声明后生成的HockeyPlayer数据类中cup_wins字段的类型即为ListString而非String。第二步实现 ColumnAdapter 并注入 Database创建Database时SQLDelight 会要求你为所有使用了自定义类型的列提供适配器。ColumnAdapterT, S只有两个方法需要实现decode(databaseValue: S): T— 把数据库侧值S解码为自定义类型T读路径encode(value: T): S— 把自定义类型T编码回数据库侧值S写路径。以把逗号分隔的TEXT映射为ListString为例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 ) )Database构造函数中的hockeyPlayerAdapter参数接收代码生成器为每张表生成的Adapter类其命名规则为「表名 .Adapter」而cup_winsAdapter正是AS声明对应的那一列。此后所有对该列的读写都会自动经过listOfStringsAdapter完成双向转换——对上层代码完全透明。ColumnAdapter接口的完整定义见 ColumnAdapter.ktdecode与encode互为逆操作接口注释明确要求数据库侧类型S必须是Long、Double、String、ByteArray之一这保证了适配器可以安全地接入 SQLite 驱动层。枚举类型Enums把枚举以字符串形式存库是常见需求。SQLDelight 运行时为此内置了EnumColumnAdapter它本质上是一个把枚举映射为String的ColumnAdapter无需你手动实现。声明与注入import com.example.hockey.HockeyPlayer; CREATE TABLE hockeyPlayer ( position TEXT AS HockeyPlayer.Position )val queryWrapper: Database Database( driver driver, hockeyPlayerAdapter HockeyPlayer.Adapter( positionAdapter EnumColumnAdapter() ) )EnumColumnAdapter()以无参形式调用时是一个inline reified工厂函数它会根据类型参数自动获取该枚举的所有取值见 EnumColumnAdapter.kt。底层实现原理EnumColumnAdapterT的编解码策略很直观EnumColumnAdapter.ktdecode在枚举值集合中查找name与数据库字符串相等的那一项enumValues.first { it.name databaseValue }encode直接取value.name作为存储字符串。也就是说数据库中保存的是枚举的name而非ordinal因此对枚举重命名会导致历史数据无法匹配迁移时需要注意这一点。此外该实现要求枚举值必须存在若数据库中出现未知字符串会抛出异常属于按名精确匹配的严格策略。Value 类型Value Types对于希望给列增加语义化包装、又不愿写完整适配器的场景SQLDelight 支持用AS VALUE声明让编译器为列自动生成一个包装底层数据库类型的 value 类型CREATE TABLE hockeyPlayer ( id INT AS VALUE );该语法对应 sqldelight.bnf 中column_type产生式的VALUE分支。从文档描述看生成的值类型会包裹该列在数据库中的底层类型从而在 Kotlin 侧获得一个区别于普通Long/Int的独立类型例如用于区分球员 id与球队 id避免传参时混用。AS LOCK语法则用于锁定该值类型防止其被隐式转换为底层原始类型。value 类型在写回数据库时仍以底层类型参与绑定因此不会改变表的物理存储结构。说明Value 类型是相对较新的语法特性具体生成代码的形态取决于你所使用的 SQLDelight 版本建议以当前版本实际生成的代码为准。总结与进一步阅读SQLDelight 的类型系统可以概括为一条主线SQLite 只有四种存储类型而 Kotlin 侧的类型由AS扩展约束决定凡是与默认四类映射不一致的类型都通过ColumnAdapter在数据库侧值与自定义类型之间桥接。从primitive-adapters的Int/Float/Short到枚举再到任意领域类型全部遵循同一套注入机制这也让生成的数据访问层既保持类型安全又具备充分的扩展空间。相关参考本文档同主题的其他方言版本android_sqlite/types.md、jvm_sqlite/types.md、multiplatform_sqlite/types.md自定义列类型的完整文档docs/common/custom_column_types.md运行时适配器接口与枚举适配器runtime/src/commonMain/kotlin/app/cash/sqldelight/ColumnAdapter.kt、runtime/src/commonMain/kotlin/app/cash/sqldelight/EnumColumnAdapter.kt原始类型适配器实现与测试adapters/primitive-adaptersAS语法产生式sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/sqldelight.bnf赞分享后端ORM【免费下载链接】sqldelightSQLDelight - Generates typesafe Kotlin APIs from SQL项目地址https://gitcode.com/gh_mirrors/sq/sqldelight点击查看免费下载相关推荐SQLDelight Native SQLite 类型系统详解默认类型映射、ColumnAdapter 与自定义列类型实战SQLDelight Native SQLite 类型系统详解默认类型映射、ColumnAdapter 与自定义列类型实战 本文面向使用 SQLDelight后端ORMSQLDelight 类型系统完全指南SQLite 内置类型、Primitive 适配器、自定义列类型与 Value Type 实战SQLDelight 类型系统完全指南SQLite 内置类型、Primitive 适配器、自定义列类型与 Value Type 实战 SQLDelight 的后端ORMSQLDelight 自定义列类型完全指南ColumnAdapter、枚举映射与 Value Types 深入解析SQLDelight 自定义列类型完全指南ColumnAdapter、枚举映射与 Value Types 深入解析 导读 SQLDelight 通过 AS 语后端ORM上一篇探索 Sketch Measure设计师的测量利器下一篇自主托管指南: 掌握你的数字主权创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

将FME拓展到ArcMap中

将FME拓展到ArcMap中

我们付出一些成本,时间的或者其他,最终总能收获一些什么。 主要参考的使用FME拓展ArcGIS和将FME集成到ArcGIS中 本人电脑上安装的为Arcmap10.8和FME2020 首先,将安装的FME中的FME integration Console 以管理员身份打开 点击下面的位置是一…

2026/10/9 1:16:53 阅读更多 →
游戏设计梦工厂 C1(完结) 游戏的核心是体验

游戏设计梦工厂 C1(完结) 游戏的核心是体验

书里的干货实在是太多了,第一章奔着5000字去了,后续会考虑适当的拆分便于阅读; 本书总共分了三个篇章: 一、游戏设计基础 二、设计一款游戏 三、像一名游戏设计师一样工作 本章C1(第一章 游戏设计师的角色)属于书里第一篇: 游戏设计基础。 0 序和综述 1 游戏设计师的…

2026/10/9 1:16:53 阅读更多 →
CloudQuery Kinesis Firehose 目标插件配置指南:从 `stream_arn` 到批量写入参数的完整解析

CloudQuery Kinesis Firehose 目标插件配置指南:从 `stream_arn` 到批量写入参数的完整解析

数据集成数据工程数据分析 【免费下载链接】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/9 1:16:53 阅读更多 →

最新新闻

AI日报从0到1:筛选逻辑、工具链配置与90分钟生产流程

AI日报从0到1:筛选逻辑、工具链配置与90分钟生产流程

1. AI日报这个栏目到底在做什么做AI日报这件事,我从2024年底开始坚持到现在,中间断更过两次,一次是因为信息源太杂导致筛选成本失控,一次是因为写得太像新闻通稿自己都不想看。后来我把整个流程重新拆了一遍,才找到可持…

2026/10/9 1:47:12 阅读更多 →
GEPA 实验追踪指南:用 TrackingConfig 把优化过程接入 WandB 与 MLflow

GEPA 实验追踪指南:用 TrackingConfig 把优化过程接入 WandB 与 MLflow

AI Agent提示工程模型优化 【免费下载链接】gepa Optimize prompts, code, and more with AI-powered Reflective Optimization 项目地址: https://gitcode.com/gh_mirrors/ge/gepa 点击查看 免费下载 本篇指南聚焦 GEPA 仓库中 optimize_anything 流程的**实验追踪…

2026/10/9 1:47:12 阅读更多 →
PWM脉宽调制原理与实战:从LED调光到电机调速的完整指南

PWM脉宽调制原理与实战:从LED调光到电机调速的完整指南

1. 内容整体设计与思路拆解1.1 从一个"闪灯"的现象说起很多人第一次接触 PWM,都是被 LED 调光这个场景带进来的。Arduino 上一个analogWrite(),51 单片机上一个定时器中断翻转 IO,灯就从全亮变成了"半亮"。但如果你直接拿…

2026/10/9 1:47:12 阅读更多 →
Apache CouchDB Nouveau:基于 Lucene 的库级全文搜索从入门到源码剖析

Apache CouchDB Nouveau:基于 Lucene 的库级全文搜索从入门到源码剖析

数据库文档数据库后端 【免费下载链接】couchdb Seamless multi-primary syncing database with an intuitive HTTP/JSON API, designed for reliability 项目地址: https://gitcode.com/gh_mirrors/co/couchdb 点击查看 免费下载 Nouveau 是 Apache CouchDB 中用于…

2026/10/9 1:47:12 阅读更多 →
Trellis 中 Skills、Commands、Prompts 与 Workflows 的差异、路径与本地化改造指南

Trellis 中 Skills、Commands、Prompts 与 Workflows 的差异、路径与本地化改造指南

桌面应用开发工具 【免费下载链接】EcoPaste 🎉跨平台的剪贴板管理工具 | Cross-platform clipboard management tool 项目地址: https://gitcode.com/gh_mirrors/ec/EcoPaste 点击查看 免费下载 在本仓库(EcoPaste,一个已由 tre…

2026/10/9 1:47:12 阅读更多 →
使用 VS Code Cache Explorer 诊断提示词缓存:定位 Cache Miss、Token 成本与延迟问题

使用 VS Code Cache Explorer 诊断提示词缓存:定位 Cache Miss、Token 成本与延迟问题

文档教程 【免费下载链接】vscode-docs Public documentation for Visual Studio Code 项目地址: https://gitcode.com/gh_mirrors/vs/vscode-docs 点击查看 免费下载 在 Visual Studio Code 的 GitHub Copilot Chat 与 Agent 工作流中,语言模型提供商会…

2026/10/9 1:46:12 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* 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 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* 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 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* 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 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* 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 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* 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 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/7 13:34:55 阅读更多 →