如何在Flutter Drift中查询多对多关系的指定账户及关联标签?
解决Flutter Moor/Drift多对多关系下按ID获取账户及关联标签的问题
原代码问题分析
- 缺少账户表关联:查询从
accountsTags中间表启动,但未关联accounts表,导致无法读取账户实体数据。 - 错误的查询结果获取方式:
getSingleOrNull()仅能返回单条关联记录,无法覆盖一个账户对应多个标签的场景。 - 单条结果无法转换为标签列表:单条查询行只能读取单个
TagDTO,无法直接得到所需的List<TagDTO>。
优化后的实现方案
方案一:单次关联查询(推荐)
通过一次关联查询获取所有相关数据,再将结果组装为AccountWithTags:
Future<AccountWithTags?> getAccountWithTagsById(int idAccount) async { // 关联账户表、中间表、标签表,查询目标账户的所有关联数据 final rows = await (select(accounts) ..where((tbl) => tbl.id.equals(idAccount))) .join([ leftOuterJoin(accountsTags, accountsTags.accountId.equalsExp(accounts.id)), leftOuterJoin(tags, tags.id.equalsExp(accountsTags.tagId)), ]).get(); if (rows.isEmpty) return null; // 提取账户信息(所有行的账户数据一致,取第一行即可) final account = rows.first.readTable(accounts); // 收集所有非空的标签数据,过滤掉无标签的情况 final tagsList = rows .map((row) => row.readTable(tags)) .where((tag) => tag != null) .cast<TagDTO>() .toList(); return AccountWithTags(account, tagsList); }
方案二:两次拆分查询(更直观)
先查询账户,再单独查询该账户关联的所有标签:
Future<AccountWithTags?> getAccountWithTagsById(int idAccount) async { // 第一步:获取目标账户 final account = await (select(accounts)..where((tbl) => tbl.id.equals(idAccount))).getSingleOrNull(); if (account == null) return null; // 第二步:查询该账户关联的所有标签 final tags = await (select(tags) ..where((tbl) => exists( select(accountsTags) ..where((at) => at.accountId.equals(idAccount) & at.tagId.equalsExp(tbl.id)), ))) .get(); return AccountWithTags(account, tags); }
中间表优化建议
为避免同一个账户与标签重复关联,建议给中间表添加唯一约束:
@DataClassName("AccountTagDTO") class AccountsTags extends Table { IntColumn get id => integer().autoIncrement()(); IntColumn get accountId => integer().references(Accounts, #id)(); IntColumn get tagId => integer().references(Tags, #id)(); // 设置复合主键,确保(accountId, tagId)组合唯一 @override Set<Column> get primaryKey => {accountId, tagId}; }
内容的提问来源于stack exchange,提问作者matteoh
相关产品推荐
相关产品推荐

