How does BaseDao handle column
class BaseTable extends Table{
BoolColumn get uploaded => boolean().withDefault(const Constant(false))();
}
class TableA extends BaseTable {
TextColumn get type => text().nullable()();
...
}
class TableB extends BaseTable {
TextColumn get name => text().nullable()();
...
}
how should i handle uploaded in BaseDao?
I'm not sure what you mean with "handle uploaded in BaseDao". Can you be more specific on what exactly you're trying to do?
Be aware that inheritance with table classes is only supported so that you can apply columns to multiple tables at once. There's no such thing as table inheritance in sql (not in sqlite, at least). So you couldn't write a @UseDao(tables: [BaseTable]) and write queries that operate on TableA and TableB, if that's what you're trying to do.
i want write a method in BaseDao like :
Future<void> setUploaded(String id) {
// change uploaded column
}
If I didn't, I'd have to write almost identical code in both places(TableADao、TableBDao、ETC...)
I have the same problem: I have used your example BaseDao:
import 'package:moor/moor.dart';
abstract class BaseDao<T extends Table, D extends DataClass,
DB extends GeneratedDatabase> extends DatabaseAccessor<DB> {
final TableInfo<T, D> _table;
BaseDao(DB db, this._table) : super(db);
Future<void> insertOne(Insertable<D> value) => into(_table).insert(value);
Future<List<D>> getAll() => select(_table).get();
Future<void> createOrUpdateEntity(Insertable<D> entity) =>
into(_table).insertOnConflictUpdate(entity);
}
So i can get all entities and create entities.
But i also need to delete entities, but at the moment i need to implement almost identical code for every entity:
@UseDao(tables: [UserEntities])
class UserDao extends BaseDao<UserEntities, UserEntity, SyncDatabase>
with _$UserDaoMixin {
UserDao(SyncDatabase db, TableInfo<UserEntities, UserEntity> table)
: super(db, table);
Future<void> deleteById(String id) {
return (delete(userEntities)..where((user) => user.id.equals(id))).go();
}
}
because the baseDao don't know that the entity has an id.
But what i want is:
abstract class EntityWithId extends DataClass {
String id;
}
abstract class BaseEntity extends Table {
TextColumn get id => text().clientDefault(() => uuid.v4())();
DateTimeColumn get deletedAt => dateTime().nullable()();
@override
Set<Column> get primaryKey => {id};
}
abstract class BaseDao<T extends BaseEntity, D extends EntityWithId,
DB extends GeneratedDatabase> extends DatabaseAccessor<DB> {
final TableInfo<T, D> _table;
BaseDao(DB db, this._table) : super(db);
Future<void> insertOne(Insertable<D> value) => into(_table).insert(value);
Future<List<D>> getAll() => select(_table).get();
Future<void> createOrUpdateEntity(Insertable<D> entity) =>
into(_table).insertOnConflictUpdate(entity);
Future<void> deleteById(String id) {
return (delete(_table)..where((user) => user.id.equals(id))).go();
}
}
Or is there another way to archieve this?
You can use
Future<int> deleteById(String id) {
final idColumn = table.columnsByName['id'] as Expression<String>;
return (delete(_table)..where((_) => idColumn.equals(id)).go();
}
Thank you for your very fast reply! Your example seems promising. I will try it out with my other use-cases and see if still need the inheritance.
My next problem is on generic inserting:
Code in my implemented Dao which i want to generalize:
Future<void> createEntryFromModel(UserEntity user) async {
final version = await getCurrentEntityVersion();
final userWithUpdatedSyncStatus =
user.copyWith(syncStatus: SyncConstants.insert, version: version);
return insert(userWithUpdatedSyncStatus);
}
Every entity in my application has these two fields. So i need to do alot of duplicate code.
I want to create a generic insert function in BaseDao.
It should insert the generic entity T:
Future<void> createEntryFromModel(T entity) async {
final version = await getCurrentEntityVersion();
final userWithUpdatedSyncStatus =
entity.copyWith(syncStatus: SyncConstants.insert, version: version); // 1.
return insert(userWithUpdatedSyncStatus); // 2.
}
The code in 1. and 2. obviously not working in my generic dao. How can i set the syncStatus- and version Column on the generic T (1.) and then insert the entity (2.)
I think it would be a similar aproach like in your delete example.
This should work, but it's some questionable Dart:
Future<void> createEntryFromModel(T entity) async {
final copy = (entity as dynamic).copyWith;
final version = await getCurrentEntityVersion();
final result = Function.apply(copy, [], {#syncStatus: SyncConstants.insert, #version: version}) as T;
return insert(result);
}
I would probably do it like this:
Future<void> createEntryFromModel(Insertable<T> entity) async {
final version = await getCurrentEntityVersion();
final columns = entity.toColumns(true);
columns['version'] = version;
columns['sync_status'] = const YourConverterForSyncConstants().mapToSql(SyncConstants.insert);
return insert(RawValuesInsertable(result));
}
Thank you for your answer! I probably missed some important information for you :-)
version is an int and SyncConstant.insert is a simple string
I implemented your suggestion:
Future<void> insertEntityWithVersionAndSyncStatus(
Insertable<D> entity, String syncStatus) async {
final version = await getCurrentEntityVersion();
final columns = entity.toColumns(true);
columns['version'] = version as Expression<int>; //my quick guess on setting version but its not working 😅
columns['sync_status'] = syncStatus as Expression<String>; //my quick guess on setting sync_status but its not working too 😅
return insert(RawValuesInsertable(columns));
}
Can you give me a hint how to set the columns?
Of course, sorry. The best way is to wrap them in variables:
columns['version'] = Variable(version);
columns['sync_status'] = Variable(syncStatus);
Thanks! I will try it out.
Here is my implementation:
Future<void> insert(Insertable<D> value) => into(_table).insert(value);
Future<void> insertEntityWithSyncStatus(
Insertable<D> entity, String syncStatus) async {
final version = await getCurrentEntityVersion();
final columns = entity.toColumns(true);
columns['sync_status'] = Variable(syncStatus);
columns['version'] = Variable(version + 1);
return insert(RawValuesInsertable(columns));
}
Future<void> insertEntity(Insertable<D> entity) {
return insertEntityWithSyncStatus(entity, SyncConstants.insert);
}
But when i use the insertEntity-Method i get a StateError (Bad state: Too many elements)-Exception.

Why is it not working?
Can you post the trace of the exception? It sounds like a .single somewhere where it's not supposed to be, but that's hard to say without checking the origin.
This is where the error occurs:

This is the function i forgot to post:
/// Gives the highest version number in the entities table
/// Returns the max-version as [int]
Future<int> getCurrentEntityVersion() async {
final versionColumn = _table.columnsByName['version'] as Expression<int>;
final maxVersionExpression = versionColumn.max();
final max = await (selectOnly(_table)..addColumns([maxVersionExpression]))
.map((row) => row.read(maxVersionExpression))
.getSingle();
if (max == null) {
return 0;
} else {
return max;
}
}
Is this function the problem? It worked in my daos without problems
Is this function the problem?
The problem actually happens during inserts, apologies for posting the broken code. Does it work if you set the type argument on Variable?
columns['sync_status'] = Variable<String>(syncStatus);
columns['version'] = Variable<int>(version + 1);
Now its working!!! Thank you for your help. Possibly i make a documentation how to create a generic Dao-Baseclass for other people. I'll see :-)
Hey i try to write like @rubiktubik code but the generated code couldn't generate constructor to Dao with TableInfo parameter
abstract class BaseDao<T extends Table, D extends DataClass,
DB extends GeneratedDatabase> extends DatabaseAccessor<DB> {
final TableInfo<T, D> _table;
BaseDao(DB db, this._table): super(db)
Future<D> getById(int id) {
final idColumn = _table.columnsByName['id'] as Expression<int>;
return (select(_table)..where((_) => idColumn.equals(id))).getSingle();
}
}
@UseDao(tables: [Users])
class UserDao extends BaseDao<Users, User, MyDatabase>
with _$UserDaoMixin {
UserDao(MyDatabase db, TableInfo<Users, User> table) : super(db, table);
}
// error in MyDatabase.g.dart generated code
........
UserDao _userDao;
UserDao get userDao => _userDao ??= UserDao(this as MyDatabase);
........
@Abdktefane What error are you getting exactly? If your database class is named GymZoneDatabase, your UserDao should extend BaseDao<Users, User, GymZoneDatabase> accordingly.
I apologize. There was a mistake in copying the code but I have corrected it.
The error is that moor generator cannot generate a constructor to BaseDao with TableInfo<T, D> _table , it only generates UserDao(MyDatabase)
code in generated MyDatabase.g.dart file when call the dao be like =>
........
UserDao _userDao;
UserDao get userDao => _userDao ??= UserDao(this as MyDatabase);
........
and thanks for fast response
Alright, now I see the error. You can fix that by looking up the right table in the constructor:
@UseDao(tables: [Users])
class UserDao extends BaseDao<Users, User, MyDatabase>
with _$UserDaoMixin {
UserDao(MyDatabase db) : super(db, db.users);
}
it works. Thank you ❤️
Now its working!!! Thank you for your help. Possibly i make a documentation how to create a generic Dao-Baseclass for other people. I'll see :-)
Any updates on this?