SqlDelight ON CONFLICT DO UPDATE 抛出near "ON"语法错误求助
问题产生原因
dialect = "sqlite:3.25"配置仅作用于SqlDelight的编译期语法检查阶段,不会将高版本SQLite的能力打包进应用,运行时Android端默认调用系统内置的SQLite实现,而系统内置SQLite版本与API等级强绑定:
- API 30(Android 11)及以上版本内置SQLite版本为3.28+,原生支持
INSERT ... ON CONFLICT DO UPDATE(UPSERT)语法,因此运行正常 - API 29及以下版本内置SQLite版本最高为3.22,未实现该UPSERT语法,执行时会直接抛出语法错误,这就是低版本设备崩溃的核心原因。
你遇到的near "ON": syntax error报错,就是低版本SQLite解析器不认识ON CONFLICT DO UPDATE结构导致的,语句中用到的excluded关键字同样是SQLite 3.25新增特性,低版本也无法识别。
可行解决方案
根据项目实际需求选择以下任意一种方案即可:
- 方案1:打包高版本SQLite到应用中,脱离系统内置SQLite版本限制
引入支持bundled模式的Android SQLite驱动(AndroidX SQLite、Requery SQLite均可),初始化SqlDelight时替换默认的系统SQLite驱动为打包了高版本SQLite二进制的驱动实例,全API等级下运行的SQLite版本统一为3.25+,原有SQL语句不需要做任何修改即可正常运行。SqlDelight 1.5.3版本对这类自定义驱动的支持已经完善,不需要额外升级SqlDelight版本。 - 方案2:改写SQL逻辑兼容低版本SQLite,不引入额外依赖
放弃使用ON CONFLICT DO UPDATE语法,改用低版本原生支持的逻辑实现UPSERT效果:- 若表结构简单、所有字段都会在插入时赋值,可以直接用
INSERT OR REPLACE语法替代,注意该语法会在冲突时删除旧行再插入新行,未赋值的字段会被重置为默认值 - 如果只需要更新部分字段,推荐用事务包裹逻辑:先尝试执行普通INSERT语句,捕获唯一键冲突异常后再执行UPDATE语句更新目标字段,更新时直接传入对应参数,不要使用
excluded关键字,这种方式不会丢失原有行的其他字段数据,全API版本兼容。
- 若表结构简单、所有字段都会在插入时赋值,可以直接用
- 方案3:调整最低支持版本
如果项目不需要覆盖API 29及以下的用户,直接将Android模块的minSdkVersion调整为30,直接使用系统内置的高版本SQLite即可,不需要修改代码或依赖。
内容的提问来源于stack exchange,提问作者Saul
相关产品推荐
相关产品推荐

