As of sembast 3.0.1, a trigger like feature is supported. It allows implementing features like "on delete cascade" to ensure data integrity.
- Triggers are called when records are added, updated or deleted.
- Triggers are called in the same transaction than the one where the record was modified
- A trigger can itself perform other modification that could itself call other triggers
- Like transactions, a trigger must be idempotent, i.e. if for some reason a transaction is re-started, pending changes are discarded and triggers will be called again.
- You can set triggers on stores.
You can listen to changes in a store
// Track record changes
store.addOnChangesListener(db).listen((transaction, changes) async {
// ...
});
In each change:
oldSnapshot
is null for item addednewSnapshot
is null for item deleted- none are null for modification
Warning: setting triggers could have write penalty if your callback is a lenghty operation. Tu use with care.
If you modify a record in the callback, the callback will be called again, so make sure you don't update indefinitely the same record.
Create a 'student' and 'enroll' store. A student can enroll a course.
var studentStore = intMapStoreFactory.store('student');
var enrollStore = intMapStoreFactory.store('enroll');
Setup trigger to delete a record in 'enroll' when a student is deleted
studentStore.addOnChangesListener(db, (transaction, changes) async {
// For each student deleted, delete the entry in enroll store
for (var change in changes) {
// newValue is null for deletion
if (change.isDelete) {
// Delete in enroll, use the transaction!
await enrollStore.delete(transaction,
finder:
Finder(filter: Filter.equals('student', change.ref.key)));
}
}
});
In an application, you would likely considered your database 'ready' when triggers are set.
Add some data
var studentId1 = await studentStore.add(db, {'name': 'Jack'});
var studentId2 = await studentStore.add(db, {'name': 'Joe'});
await enrollStore.add(db, {'student': studentId1, 'course': 'Math'});
await enrollStore.add(db, {'student': studentId2, 'course': 'French'});
await enrollStore.add(db, {'student': studentId1, 'course': 'French'});
// The initial data in enroll is
expect((await enrollStore.find(db)).map((e) => e.value), [
{'student': 1, 'course' : 'Math'},
{'student': 2, 'course': 'French'},
{'student': 1, 'course': 'French'}
]);
Delete the student. It will trigger the listener and the entries in the enroll store will be deleted.
await studentStore.record(studentId1).delete(db);
// Data has been deleted in 'enroll' store too!
expect((await enrollStore.find(db)).map((e) => e.value), [
{'student': 2, 'course': 'French'},
]);