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

Flutter Isar数据库嵌套类存储异常:仅ProductGroup保存成功

Isar数据库仅保存ProductGroup数据,关联子对象未存储问题解决

问题描述

我定义了4个关联的数据类:ProductGroup、ProductVariant、ProductSize和ProductColor,通过IsarLinks建立嵌套关联关系。在将JSON数据解析后保存到Isar数据库的过程中,仅ProductGroup类的数据成功存储,ProductVariant、ProductSize和ProductColor的数据均未被保存。

相关代码

product_group.dart

@Collection()
class ProductGroup {
  late Id id;

  @Index(caseSensitive: false)
  late String productGroupName;

  final productVariants = IsarLinks<ProductVariant>();

  ProductGroup();

  ProductGroup.fromJson(Map<String, dynamic> json) {
    id = json['Id'];
    productGroupName = json['PG'];
    if (json['Ps'] != null) {
      json['Ps'].forEach((variant) {
        productVariants.add(ProductVariant.fromJson(variant));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['Id'] = id;
    data['PG'] = productGroupName;
    data['Ps'] = productVariants.map((variant) => variant.toJson()).toList();
    return data;
  }
}

product_variant.dart

@Collection()
class ProductVariant {
  late Id id;
  late String variantName;
  final productSizes = IsarLinks<ProductSize>();

  ProductVariant();

  ProductVariant.fromJson(Map<String, dynamic> json) {
    id = json['Id'];
    variantName = json['St'];
    if (json['Ss'] != null) {
      json['Ss'].forEach((v) {
        productSizes.add(ProductSize.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['Id'] = id;
    data['St'] = variantName;
    data['Ss'] = productSizes.map((v) => v.toJson()).toList();
    return data;
  }
}

product_size.dart

@Collection()
class ProductSize {
  late Id id;
  late String size;
  final productColors = IsarLinks<ProductColor>();

  ProductSize();

  ProductSize.fromJson(Map<String, dynamic> json) {
    id = json['Id'];
    size = json['S'];
    if (json['Cs'] != null) {
      json['Cs'].forEach((v) {
        productColors.add(ProductColor.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['Id'] = id;
    data['S'] = size;
    data['Cs'] = productColors.map((color) => color.toJson()).toList();
    return data;
  }
}

product_color.dart

@Collection()
class ProductColor {
  late Id id;
  late String colorName;
  late String colorHexCode;

  ProductColor();

  ProductColor.fromJson(Map<String, dynamic> json) {
    id = json['Id'];
    colorName = json['C'];
    colorHexCode = json['CC'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['Id'] = id;
    data['C'] = colorName;
    data['CC'] = colorHexCode;
    return data;
  }
}

JSON解析与保存代码

convertJsonToIsar() async {
    try {
      // 测试用,从assets加载JSON,生产环境从服务器获取
      final String response = await rootBundle.loadString('assets/pr_dump.json');
      final data = await json.decode(response);

      List<ProductGroup> productGroupList = [];
      data.forEach((item) {
        productGroupList.add(ProductGroup.fromJson(item));
      });

      Isar _isar = getIsar();

      _isar.writeTxnSync(() {
         _isar.productGroups.putAllSync(productGroupList, saveLinks: true);
      });

    } catch (e) {
      // 错误处理
      print('捕获到错误');
      print(e.toString());
      return 0;
    }
  }

解决方案

问题根源

Isar的IsarLinks关联依赖于子对象已存在于数据库中,saveLinks: true仅负责保存当前对象与已入库子对象的关联关系,不会自动递归保存未入库的嵌套子对象。原代码只保存了ProductGroup,未手动保存其下的ProductVariant、ProductSize和ProductColor,导致这些子对象无法被持久化。

修复步骤

  1. 递归逐层保存所有嵌套对象
    在写入事务中,从最底层的ProductColor开始,逐层向上保存到ProductGroup,确保每个层级的对象都先入库,再建立关联:

    修改后的保存代码:

    convertJsonToIsar() async {
        try {
          final String response = await rootBundle.loadString('assets/pr_dump.json');
          final data = await json.decode(response);
    
          List<ProductGroup> productGroupList = [];
          data.forEach((item) {
            productGroupList.add(ProductGroup.fromJson(item));
          });
    
          Isar _isar = await getIsar();
    
          await _isar.writeTxn(() async {
            // 先保存最底层的ProductColor
            for (var group in productGroupList) {
              for (var variant in group.productVariants) {
                for (var size in variant.productSizes) {
                  await _isar.productColors.putAll(size.productColors);
                }
                // 保存ProductSize
                await _isar.productSizes.putAll(variant.productSizes);
              }
              // 保存ProductVariant
              await _isar.productVariants.putAll(group.productVariants);
            }
            // 最后保存ProductGroup,自动建立关联
            await _isar.productGroups.putAll(productGroupList, saveLinks: true);
          });
    
        } catch (e) {
          print('捕获到错误');
          print(e.toString());
          return 0;
        }
      }
    
  2. 确保Isar实例注册所有集合
    检查Isar初始化代码,确认4个集合都已注册,否则数据库无法识别这些对象类型:

    import 'package:isar/isar.dart';
    import 'package:path_provider/path_provider.dart';
    
    Future<Isar> getIsar() async {
      if (Isar.instanceNames.isEmpty) {
        final dir = await getApplicationDocumentsDirectory();
        return await Isar.open(
          [ProductGroupSchema, ProductVariantSchema, ProductSizeSchema, ProductColorSchema],
          directory: dir.path,
        );
      }
      return Isar.getInstance()!;
    }
    
  3. 验证ID的有效性

    • 确保JSON中的Id是唯一非0整数,Isar默认自增ID为0时会自动生成新ID,若手动设置ID需避免重复。
    • 如果不需要保留JSON中的ID,可去掉构造函数中id = json['Id']的赋值,让Isar自动生成唯一ID,避免冲突。

关键说明

Isar的关联是基于对象ID的引用关系,只有子对象已存在于数据库中,父对象的IsarLinks才能正确保存关联。因此必须按照从底层到顶层的顺序,先保存所有子对象,再保存父对象并建立关联。


内容的提问来源于stack exchange,提问作者Haaris Ahamed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:25:32