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

如何在Drift中通过子查询实现Item与Attribute的多对多关联

在Drift ORM中结合dart_mappable实现Item与Attribute的多对多关联

首先先修正你代码里的两处关键问题:

  • 原Tags表应改名为Attributes,对应你的Attribute模型,避免概念混淆
  • 关联表ItemAttributeEntries的引用要指向表类而非模型类,同时要设置复合主键避免重复关联

修正后的表结构定义

import 'package:drift/drift.dart';
import 'package:dart_mappable/dart_mappable.dart';

// 假设你的Item模型是dart_mappable生成的,结构类似:
@MappableClass()
class Item with ItemMappable {
  final int id;
  // 其他字段...
  final List<Attribute> attributes;

  Item({required this.id, required this.attributes, /* 其他参数 */});
}

@MappableClass()
class Attribute with AttributeMappable {
  final int id;
  // 其他字段...

  Attribute({required this.id, /* 其他参数 */});
}

// Drift表定义
@UseRowClass(Item)
class Items extends Table {
  IntColumn get id => integer().unique()();
  // 对应Item模型的其他字段,注意:表类只定义数据库列,不能直接放List<Attribute>
}

@UseRowClass(Attribute)
class Attributes extends Table { // 替换原Tags表
  IntColumn get id => integer().unique()();
  // 对应Attribute模型的其他字段
}

@DataClassName('ItemAttributeEntry')
class ItemAttributeEntries extends Table {
  IntColumn get itemId => integer().references(Items, #id)();
  IntColumn get attributeId => integer().references(Attributes, #id)();

  // 设置复合主键,确保同一关联只存一次
  @override
  Set<Column> get primaryKey => {itemId, attributeId};
}

实现多对多关联查询的几种方式

1. 单Item查询:子查询+批量获取属性

适合只需要加载单个Item及其属性的场景,逻辑简单直接:

class MyDao extends DatabaseAccessor<MyDatabase> with _$MyDaoMixin {
  MyDao(MyDatabase db) : super(db);

  Future<Item?> getItemWithAttributes(int itemId) async {
    // 先查询Item本身
    final item = await (select(items)..where((t) => t.id.equals(itemId))).getSingleOrNull();
    if (item == null) return null;

    // 子查询获取该Item关联的所有attributeId
    final attributeIds = await (select(itemAttributeEntries)
          ..where((t) => t.itemId.equals(itemId))
          ..map((t) => t.attributeId))
        .get();

    // 通过ID列表查询对应的Attribute
    final attributes = await (select(attributes)..where((t) => t.id.isIn(attributeIds))).get();

    // 利用dart_mappable生成的copyWith方法组装带属性的Item
    return item.copyWith(attributes: attributes);
  }
}

2. 批量查询:联表+分组组装

如果要一次性加载所有Item及其属性,用联表查询后分组处理更高效:

Future<List<Item>> getAllItemsWithAttributes() async {
  // 联表查询:Items -> 关联表 -> Attributes
  final query = select(items).join([
    leftOuterJoin(itemAttributeEntries, itemAttributeEntries.itemId.equalsExp(items.id)),
    leftOuterJoin(attributes, attributes.id.equalsExp(itemAttributeEntries.attributeId)),
  ]);

  final rows = await query.get();
  // 按ItemID分组,收集对应的Attribute
  final itemGroups = <int, _ItemAttributeGroup>{};

  for (final row in rows) {
    final item = row.readTable(items);
    final attribute = row.readTableOrNull(attributes);

    itemGroups.putIfAbsent(item.id, () => _ItemAttributeGroup(item))
      ..attributes.addIfNotNull(attribute);
  }

  // 转换为带属性的Item列表
  return itemGroups.values.map((group) => group.item.copyWith(attributes: group.attributes)).toList();
}

// 辅助类用于分组累加属性
class _ItemAttributeGroup {
  final Item item;
  final List<Attribute> attributes = [];

  _ItemAttributeGroup(this.item);

  void addIfNotNull(Attribute? attr) {
    if (attr != null) attributes.add(attr);
  }
}

关键注意事项

  • 确保Item模型有copyWith方法:dart_mappable生成的模型默认会提供此方法,用于修改attributes字段
  • 字段映射一致性:如果模型字段和数据库列名不一致,需要用@MappableField(key: "列名")指定映射关系
  • 性能优化:批量查询时尽量用联表+分组,减少数据库请求次数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:53:20