HarmonyOS鸿蒙Next中relationalStore到底支不支持foreign_keys?

HarmonyOS鸿蒙Next中relationalStore到底支不支持foreign_keys? 请教一下大佬们: https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-data-relationalstore-rdbstore#execute12 这个接口明确说明“支持执行PRAGMA语法的sql”,但是我执行 PRAGMA foreign_keys = ON; 后好像并没有实际启用外键约束;这个模块又好像把 compile_options 给屏蔽了,也是一眼黑。 四处查阅了一下,官方文档好像并没有明确是否支持外键;相关的帖子也是少之又少,所以还是想问问大佬们。


更多关于HarmonyOS鸿蒙Next中relationalStore到底支不支持foreign_keys?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

8 回复

你好,据我了解SQLite数据库启用外键后,需要创建新表,已经存在的表再加外键是不支持的。还是建议从业务逻辑方面去关联多个表之间数据关联关系,加外键会引起很多问题。

更多关于HarmonyOS鸿蒙Next中relationalStore到底支不支持foreign_keys?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


谢谢大佬,我已经确定问题了:通过 RDB.createTransaction() 创建的 transaction 对象内部上下文不支持 FK 能力,所以事务内外键约束全部失效。
我这小项目,就还是实现效率和数据正确性先行了😁,

厉害,向你学习~

后面看到这个帖子的兄弟看这里:写了个测试项目实测了一遍,最终结论:

HarmonyOS relationalStore 的 RdbStore 主连接对 SQLite foreign_keys 支持较完整,包括 PRAGMA foreign_keys、foreign_key_list、foreign_key_check,以及 CASCADE、RESTRICT、SET NULL、ON UPDATE CASCADE 等外键动作。

事务方面存在差异:旧式 RdbStore.beginTransaction()/commit()/rollBack() 能继承并执行 foreign_keys 约束;但 createTransaction()/Transaction.execute() 创建的事务上下文不会继承 foreign_keys=ON,且在事务内执行 PRAGMA foreign_keys=ON 后仍为 0,导致外键违规写入不会被拦截。

因此,依赖 foreign key enforcement 的事务逻辑应使用旧式 beginTransaction()/commit()/rollBack(),不要使用 createTransaction()。

学到了,为你点赞!

relationalStore 底层是 SQLite,所以“语法层面”当然能写 FOREIGN KEY (...) REFERENCES ...,文档也确实把“外键”列在 SQLite 特性里(说明能力目标上是支持的)。

但你遇到的 “PRAGMA foreign_keys = ON; 执行了却不生效”,在实际工程里最常见的原因不是编译选项,而是:

关键点:PRAGMA foreign_keys 是“连接级别”的,而 RdbStore 有连接池

SQLite 的外键开关是 per-connection 的:你在哪个连接上 PRAGMA foreign_keys=ON,只对那个连接生效。
而 HarmonyOS/OpenHarmony 的 RdbStore 常驻有多条读连接 + 1 条写连接(连接池),你在某次 store.execute() 上执行 PRAGMA,很可能只命中了某一条连接;后续 insert/update/delete 又走到了另一条连接(外键仍是 OFF),于是你感觉“没启用”。

这也解释了你看到的现象:接口说支持 PRAGMA,但“全局外键约束”看起来没打开。


你可以这样验证到底是不是这个原因

  1. 先执行:

    PRAGMA foreign_keys;
    

    看返回是 0 还是 1(不同接口返回形式略有差异,但原则是要能读出当前连接的状态)。

  2. 做一个最小用例验证约束是否触发:

  3. 建 parent/child 两张表(child 声明 foreign key)

  4. 往 child 插入一个不存在的 parent_id

  5. 如果外键真的开启,应报 FOREIGN KEY constraint failed


  6. 可落地的规避方案(推荐):把“启用外键 + 相关写操作”放在同一个 Transaction 里

    createTransaction() 往往会让后续 SQL 落在同一条连接上(至少在该事务上下文内更可控),你可以这样做:

    const tx = await store.createTransaction({});
    await tx.execute('PRAGMA foreign_keys = ON');
    await tx.execute('PRAGMA foreign_keys'); // 可选:确认=1
    
    // 在同一个 tx 里做建表/插入/更新/删除
    await tx.execute(`INSERT INTO child(parent_id, ...) VALUES (?, ...)`, [123]);
    
    await tx.commit();
    

    如果你用事务包住后外键开始生效,基本就能确认:问题是连接池导致的“PRAGMA 不全局”。


    如果你希望“整个应用里永远开启外键”

    目前 ArkTS 的 relationalStore 没有公开一个“全局默认开启 foreign_keys”的配置项(你看到 compile_options 也拿不到是同一类原因:被封装了)。所以你通常只能选:

  7. 业务上强依赖外键约束:把关键写操作尽量放到同一个 Transaction 中,并在事务开始时执行 PRAGMA foreign_keys=ON

  8. 不依赖外键:用触发器(trigger)/应用层校验/级联删除逻辑替代(更可控、跨连接不受影响)。

  9. Sources:

  10. OpenHarmony 文档:关系型数据库基于 SQLite,支持事务、索引、视图、触发器、外键等特性(说明“外键”能力在目标范围内):https://github.com/openharmony-rs/openharmony-docs/blob/master/zh-cn/application-dev/database/data-persistence-by-rdb-store.md


HarmonyOS Next 的 relationalStore 支持 foreign_keys,但默认处于禁用状态。需要在创建或打开数据库后执行 PRAGMA foreign_keys = ON 启用外键约束,否则外键声明不生效。

HarmonyOS Next 的 relationalStore 目前不支持外键约束。虽然 execute 接口允许执行 PRAGMA 语句,但 foreign_keys 开关被底層禁用,执行 PRAGMA foreign_keys = ON; 并不会实际启用外键约束,且 compile_options 查询已被屏蔽,无法确认编译时是否包含外键支持。官方文档中未声明支持外键,不建议在产品代码中依赖该特性。如需保证关联数据一致性,请自行在事务中实现应用层校验。

回到顶部