You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用sqflite为嵌套模型创建独立关联表,无需扁平化或转JSON/字符串

sqflite 嵌套模型独立关联表实现方案

方案核心是通过外键关联三张独立表,封装事务保证嵌套数据插入的一致性,完全不需要将嵌套数据转JSON或者扁平化存储。


第一步:定义三张表的结构

三张表分别对应Collection、Translations、Question实体,通过外键建立关联,sqflite 本身不支持布尔类型,统一用0/1存储布尔值:

-- 开启外键支持,需要在数据库初始化时执行
PRAGMA foreign_keys = ON;

-- 1. 翻译表:存储所有多语言内容
CREATE TABLE translations (
  id TEXT PRIMARY KEY,
  zh_cn TEXT,
  en_us TEXT,
  -- 其他你需要的语言字段按需补充
);

-- 2. 问题表:和Collection是一对多关系,通过collection_id关联
CREATE TABLE questions (
  id TEXT PRIMARY KEY,
  collection_id TEXT NOT NULL,
  -- 你Question实体的其他字段,比如content、type等按需补充
  FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE
);

-- 3. 集合表:通过name_id、description_id关联翻译表
CREATE TABLE collections (
  id TEXT PRIMARY KEY,
  workspace_code TEXT,
  code TEXT,
  author TEXT,
  modifier TEXT,
  creation_time TEXT,
  modification_time TEXT,
  unit_code TEXT,
  shared INTEGER,
  version INTEGER,
  name_id TEXT,
  description_id TEXT,
  resource_type TEXT,
  FOREIGN KEY (name_id) REFERENCES translations(id) ON DELETE CASCADE,
  FOREIGN KEY (description_id) REFERENCES translations(id) ON DELETE CASCADE
);

-- 如果permissions属性也不想转JSON存储,可以额外建关联表
CREATE TABLE collection_permissions (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  collection_id TEXT NOT NULL,
  permission TEXT NOT NULL,
  FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE
);

ON DELETE CASCADE配置的作用是删除Collection时,自动删除关联的翻译、问题、权限数据,不需要手动清理冗余数据。


第二步:封装插入逻辑,事务保证一致性

所有嵌套数据的插入操作放在同一个事务中执行,只要有一步失败就全部回滚,避免出现脏数据:

import 'package:uuid/uuid.dart';

Future<void> insertCollection(Collection collection) async {
  final db = await yourDatabaseInstance; // 替换为你自己的sqflite实例获取逻辑
  await db.transaction((txn) async {
    // 1. 插入name对应的翻译记录,生成唯一id
    final nameId = const Uuid().v4();
    await txn.insert('translations', {
      'id': nameId,
      'zh_cn': collection.name?.zhCn,
      'en_us': collection.name?.enUs,
      // 其他语言字段对应赋值
    });

    // 2. 插入description对应的翻译记录,生成唯一id
    final descId = const Uuid().v4();
    await txn.insert('translations', {
      'id': descId,
      'zh_cn': collection.description?.zhCn,
      'en_us': collection.description?.enUs,
    });

    // 3. 插入Collection主表数据
    await txn.insert('collections', {
      'id': collection.id,
      'workspace_code': collection.workspaceCode,
      'code': collection.code,
      'author': collection.author,
      'modifier': collection.modifier,
      'creation_time': collection.creationTime,
      'modification_time': collection.modificationTime,
      'unit_code': collection.unitCode,
      'shared': collection.shared == true ? 1 : 0,
      'version': collection.version,
      'name_id': nameId,
      'description_id': descId,
      'resource_type': collection.resourceType,
    });

    // 4. 批量插入关联的Question列表
    if (collection.question != null && collection.question!.isNotEmpty) {
      for (final q in collection.question!) {
        await txn.insert('questions', {
          'id': q.id,
          'collection_id': collection.id,
          // Question其他字段对应赋值
        });
      }
    }

    // 5. 批量插入关联的权限列表(如果用了独立权限表)
    if (collection.permissions != null && collection.permissions!.isNotEmpty) {
      for (final p in collection.permissions!) {
        await txn.insert('collection_permissions', {
          'collection_id': collection.id,
          'permission': p,
        });
      }
    }
  });
}

第三步:查询自动组装嵌套实体

查询时通过多表联查或者分表查询,把关联数据直接组装成你需要的嵌套结构即可:

Future<Collection?> getCollectionById(String collectionId) async {
  final db = await yourDatabaseInstance;
  // 联查Collection主表和两个翻译表的内容
  final collectionResult = await db.rawQuery('''
    SELECT 
      c.*,
      t1.zh_cn as name_zh, t1.en_us as name_en,
      t2.zh_cn as desc_zh, t2.en_us as desc_en
    FROM collections c
    LEFT JOIN translations t1 ON c.name_id = t1.id
    LEFT JOIN translations t2 ON c.description_id = t2.id
    WHERE c.id = ?
  ''', [collectionId]);

  if (collectionResult.isEmpty) return null;
  final baseData = collectionResult.first;

  // 查询关联的Question列表
  final questionResult = await db.query(
    'questions',
    where: 'collection_id = ?',
    whereArgs: [collectionId],
  );
  final questions = questionResult.map((q) => Question.fromMap(q)).toList();

  // 查询关联的权限列表
  final permissionResult = await db.query(
    'collection_permissions',
    where: 'collection_id = ?',
    whereArgs: [collectionId],
  );
  final permissions = permissionResult.map((p) => p['permission'] as String).toList();

  // 组装为嵌套的Collection实体
  return Collection(
    id: baseData['id'] as String,
    workspaceCode: baseData['workspace_code'] as String?,
    code: baseData['code'] as String?,
    author: baseData['author'] as String?,
    modifier: baseData['modifier'] as String?,
    creationTime: baseData['creation_time'] as String?,
    modificationTime: baseData['modification_time'] as String?,
    unitCode: baseData['unit_code'] as String?,
    shared: baseData['shared'] == 1,
    version: baseData['version'] as int?,
    name: Translations(
      zhCn: baseData['name_zh'] as String?,
      enUs: baseData['name_en'] as String?,
    ),
    description: Translations(
      zhCn: baseData['desc_zh'] as String?,
      enUs: baseData['desc_en'] as String?,
    ),
    question: questions,
    permissions: permissions,
    resourceType: baseData['resource_type'] as String?,
  );
}

内容的提问来源于stack exchange,提问作者Aabhash Rai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 17:39:01