如何在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
相关产品推荐
相关产品推荐

