floor icon indicating copy to clipboard operation
floor copied to clipboard

Query parameter for generic is not supported from 1.2.0

Open wangmir opened this issue 3 years ago • 5 comments

I'm using BaseDao something like this.

abstract class BaseDao<T> {
  @Insert(onConflict: OnConflictStrategy.replace)
  Future<void> insert(T obj);

  @Insert(onConflict: OnConflictStrategy.replace)
  Future<void> inserts(List<T> objs);

  @Update(onConflict: OnConflictStrategy.replace)
  Future<void> update(T obj);

  @delete
  Future<void> remove(T obj);

  @Query("SELECT * FROM \$dbTable WHERE id = :id")
  Future<T?> findById(int id);

  @Query("SELECT * FROM \$dbTable")
  Future<List<T>> findAll();
}

And this BaseDao is used by actual implementation like,

@dao
abstract class BuildingDao extends BaseDao<BuildingEntity> {
  final dbTable = "BuildingEntity";

  @Query("SELECT * FROM \$dbTable WHERE slug = :slug")
  Future<BuildingEntity?> getBuilding(String slug);
}

And, the generated code for this was something like


  @override
  Future<BuildingEntity?> getBuilding(String slug) async {
    return _queryAdapter.query('SELECT * FROM $dbTable WHERE slug = ?1',
        mapper: (Map<String, Object?> row) => BuildingEntity(
            row['id'] as int,
            row['slug'] as String,
            row['storeId'] as String?,
            row['name'] as String,
            row['desc'] as String),
        arguments: [slug]);
  }

  @override
  Future<BuildingEntity?> findById(int id) async {
    return _queryAdapter.query('SELECT * FROM $dbTable WHERE id = ?1',
        mapper: (Map<String, Object?> row) => BuildingEntity(
            row['id'] as int,
            row['slug'] as String,
            row['storeId'] as String?,
            row['name'] as String,
            row['desc'] as String),
        arguments: [id]);
  }

But from 1.2.0, \$dbTable is not available for query... the generated query didn't remove slash from that so g file was like,

  @override
  Future<BuildingEntity?> getBuilding(String slug) async {
    return _queryAdapter.query('SELECT * FROM \$dbTable WHERE slug = ?1',
        mapper: (Map<String, Object?> row) => BuildingEntity(
            row['id'] as int,
            row['slug'] as String,
            row['storeId'] as String?,
            row['name'] as String,
            row['desc'] as String),
        arguments: [slug]);
  }

  @override
  Future<BuildingEntity?> findById(int id) async {
    return _queryAdapter.query('SELECT * FROM \$dbTable WHERE id = ?1',
        mapper: (Map<String, Object?> row) => BuildingEntity(
            row['id'] as int,
            row['slug'] as String,
            row['storeId'] as String?,
            row['name'] as String,
            row['desc'] as String),
        arguments: [id]);
  }

So it causes sqliteException.

How can i solve this problem from 1.2.0?

wangmir avatar Jun 08 '22 09:06 wangmir

Honestly, cleaning up the back slash character looks like a bug in the code_builder package, which has been fixed in recent versions. Basically in Android Room integration, using variable table name in DAO is solved by using RawQuery integration in BaseDao. I want to implement this soon, so I would suggest a workaround for you now.

  • fork the project repo
  • replace the line 160 in floor_generator/lib/writer/query_method_writer.dart by the next code:

return code.toString().replaceAll(r'\$', r'$');

dkaera avatar Jun 17 '22 10:06 dkaera

One good solution would be, if child class @Dao annotation would have parameter "tableName" where value is replacing specific abstract string (for example $dbTable or whatever else in @Query('SELECT * FROM $dbTable WHERE id = ?1)') in base Dao class. Therefore generic queries like findById could be abstracted.

ebelevics avatar Jul 15 '22 03:07 ebelevics

@ebelevics The main problem here is that we cannot use any variables in the annotations, they can only be constant.

dkaera avatar Jul 15 '22 08:07 dkaera

static const String tableName = "foo_table"

@dao(tableName: tableName)
abstract class PersonDao extends AbstractDao<Person> {
}

abstract class AbstractDao<T> {
  //[FLOOR_TABLE_NAME] is being replaced during generation with respective tableName if exists
  @Query('SELECT * FROM [FLOOR_TABLE_NAME] WHERE id = :id')
  Future<Person?> findPersonById(int id);

  @insert
  Future<void> insertItem(T item);
}

meaning if there is table name specified then generator is replacing specific floor_table_name_key with given tableName, if not then it just executes SELECT * FROM [FLOOR_TABLE_NAME] WHERE id = :id as query. I have no idea to be honest how build runner generates code, but it feels like if you can get from Child Dao annotation value, then you can replace it just raw query where "[FLOOR_TABLE_NAME]" is present (the key name is just for example). But like I said I have inspect in detail how build_runner works.

ebelevics avatar Jul 15 '22 09:07 ebelevics

@ebelevics yes, I understand your idea, but if we take a look at Android Room integration they provide this option only by RawQuery. The main idea of this project is to make it easy for an Android developer knowing Room to switch to Flutter.

I think the RawQuery integration should solve a lot of issues in one go.

dkaera avatar Jul 15 '22 10:07 dkaera

solved

dkaera avatar Feb 13 '23 08:02 dkaera

Hi, It's not resolved and the problem exists

alimcomp avatar Mar 10 '24 05:03 alimcomp