In my Flutter project, I use Isar for my local database (version 3.1.0+1
).
In my app, I have groups and tags, with a 1-n relationship (one group can have zero, one or multiple tags).
I want to add a group with its tags in a single transaction, so here is what I did:
late GroupsDAO _groupsDAO;
@override
void initState()
{
super.initState();
_groupsDAO = GroupsDAO(openDB());
}
void _addItemAndSubItems()
{
final group = Group()
..label = "My group"
..tags.add(Tag()..label = "My tag");
print("tags : ${group.tags.length}");
_groupsDAO.addOrUpdate(group);
}
@override
Widget build(BuildContext context)
{
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Isar 1-n test"),
),
body: Container(),
floatingActionButton: FloatingActionButton(
onPressed: _addItemAndSubItems,
child: const Icon(Icons.arrow_forward),
),
);
}
For your information, here are the entities:
part 'group.g.dart';
@Collection()
class Group
{
Id id = Isar.autoIncrement;
@Index(unique: true)
late String label;
final tags = IsarLinks<Tag>();
}
part 'tag.g.dart';
@Collection()
class Tag
{
Id id = Isar.autoIncrement;
late String label;
@Backlink(to: "tags")
final group = IsarLink<Group>();
}
And here is the GroupsDAO
class:
class GroupsDAO
{
final Future<Isar> db;
GroupsDAO(this.db);
Future<void> addOrUpdate(Group group) async
{
final isar = await db;
await isar.writeTxn(() async {
await isar.tags.putAll(group.tags.toList());
await isar.groups.put(group);
await group.tags.save();
});
}
}
And the openDB()
function:
Future<Isar> openDB() async
{
if (Isar.instanceNames.isEmpty)
{
final dbFolder = await getApplicationDocumentsDirectory();
return await Isar.open(
[TagSchema, GroupSchema],
inspector: true,
directory: dbFolder.path,
);
}
return Future.value(Isar.getInstance());
}
And here is the full reproductible example.
But when I try to add a group, I get the following error, even though I believe is what I already did:
Unhandled Exception: IsarError: Object "Instance of 'Tag'" has no id and can therefore not be linked. Make sure to .put() objects before you use them in links.
Also, for some reason, the print("tags : ${group.tags.length}");
line always print tags : 0
, and I don't understand why.
I think I missed something simple but I don't know what.
UPDATE
I somehow managed to add my group and my tag by doing the following:
final id = await _groupsDAO.addTag(Tag()..label = "My tag");
final group = Group()
..label = "My group"
..tags.add(Tag()..id = id..label = "My tag");
_groupsDAO.addOrUpdate(group);
and the addTag()
function:
Future<int> addTag(Tag tag) async
{
final isar = await db;
return await isar.writeTxn(() async {
return await isar.tags.put(tag);
});
}
and the addOrUpdate()
function:
Future<void> addOrUpdate(Group group) async
{
final isar = await db;
await isar.writeTxn(() async {
await isar.groups.put(group);
await group.tags.save();
});
}
But this is not a satisfying solution since I don't take advantage of any transaction: if I get an error when adding a group, I still get an orphaned tag, which is not what I want.
So how can I add a group with its tags in a transaction using Isar?
Thanks for your help.