具有Android Room的可重用通用基类DAO

pqv*_*vst 22 android android-room android-architecture-components

有没有办法用Android Room创建可重用的通用基类DAO?

public interface BaseDao<T> {

  @Insert
  void insert(T object);

  @Update
  void update(T object);

  @Query("SELECT * FROM #{T} WHERE id = :id")
  void findAll(int id);

  @Delete
  void delete(T object);

}

public interface FooDao extends BaseDao<FooObject> { ... }

public interface BarDao extends BaseDao<BarEntity> { ... }
Run Code Online (Sandbox Code Playgroud)

我无法找到任何方法来实现这一点,而无需声明相同的接口成员并为每个子类编写查询.在处理大量类似的DAO时,这变得非常乏味......

Nat*_*tan 9

今天,2017年8月8日,版本1.0.0-alpha8下面的Dao工作.我可以让其他Dao英雄级别的GenericDao.

@Dao
public interface GenericDao<T> {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(T... entity);

    @Update
    void update(T entity);

    @Delete
    void delete(T entity);
}
Run Code Online (Sandbox Code Playgroud)

但是,GenericDao不能包含在我的Database类中


Clx*_*lxy 5

我有一个 findAll 的解决方案。

该 BaseDao 中的代码:

...
public List<T> findAll() {
    SimpleSQLiteQuery query = new SimpleSQLiteQuery(
        "select * from " + getTableName()
    );
    return doFindAll(query);
}
...
public String getTableName() {
    // Below is based on your inheritance chain
    Class clazz = (Class)
        ((ParameterizedType) getClass().getSuperclass().getGenericSuperclass())
            .getActualTypeArguments()[0];
    // tableName = StringUtil.toSnakeCase(clazz.getSimpleName());
    String tableName = clazz.getSimpleName();
    return tableName;
}
...
@RawQuery
protected abstract List<T> doFindAll(SupportSQLiteQuery query);
Run Code Online (Sandbox Code Playgroud)

和其他 Dao 看起来像:

@Dao
public abstract class UserDao extends AppDao<User> {
}
Run Code Online (Sandbox Code Playgroud)

就这样

这个想法是

  1. 在运行时获取子类泛型类型的表名
  2. 将该表名传递给 RawQuery

如果你更喜欢接口而不是抽象类,你可以尝试 java 8 的可选方法。

正如你所看到的,它并不漂亮但有效。

我在这里创建了一个要点


jc1*_*c12 5

通用的 findAll 函数:

基础存储库和 dao:

abstract class BaseRepository<T>(private val entityClass: Class<T>) {

    abstract val dao: BaseDao<T>

    fun findAll(): List<T> {
        return dao.findAll(SimpleSQLiteQuery("SELECT * FROM ${DatabaseService.getTableName(entityClass)}"))
    }
}

interface BaseDao<T> {

    @RawQuery
    fun findAll(query: SupportSQLiteQuery): List<T>
}
Run Code Online (Sandbox Code Playgroud)

数据库服务:

object DatabaseService {

    fun getEntityClass(tableName: String): Class<*>? {
        return when (tableName) {
            "SomeTableThatDoesntMatchClassName" -> MyClass::class.java
            else -> Class.forName(tableName)
        }
    }

    fun getTableName(entityClass: Class<*>): String? {
        return when (entityClass) {
            MyClass::class.java -> "SomeTableThatDoesntMatchClassName"
            else -> entityClass.simpleName
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

示例 repo 和 dao:

class UserRepository : BaseRepository<User>(User::class.java) {

    override val dao: UserDao
        get() = database.userDao

}

@Dao
interface UserDao : BaseDao<User>
Run Code Online (Sandbox Code Playgroud)


Jac*_*ton 0

虽然我同意你的想法,但答案是否定的。有几个原因。

  1. fooDao_Impl.java从您的类生成 时@Dao fooDao extends BaseDao<Foo>,您将遇到很多“找不到类符号 T”错误。这是由于 Room 用于生成 dao 实现的方法所致。这种方法不会支持您想要的结果,并且不太可能很快改变(在我看来,由于类型擦除)。

  2. 即使这个问题得到解决,Room 也不支持动态@Dao查询,以防止 SQL 注入。这意味着您只能动态地将值插入到查询中,而不能动态地将列名、表名或查询命令插入到查询中。在您的示例中,您不能使用,#{T}因为它会违反此原则。理论上,如果第 1 点中详述的问题得到解决,您可以用户插入、删除和更新。