29

有什么方法可以使用 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> { ... }

在不必声明相同的接口成员并为每个子类编写查询的情况下,我无法想出任何方法来实现这一点。当处理大量类似的 DAO 时,这变得非常乏味......

4

6 回答 6

14

今天,2017 年 8 月 8 日,使用1.0.0-alpha8版本,下面的 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);
}

但是,GenericDao 不能包含在我的数据库类中

于 2017-08-08T22:00:44.027 回答
6

通用 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>
}

数据库服务:

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
        }
    }
}

示例 repo 和 dao:

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

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

}

@Dao
interface UserDao : BaseDao<User>
于 2019-06-07T16:40:06.447 回答
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);

和其他道看起来像:

@Dao
public abstract class UserDao extends AppDao<User> {
}

就这样

这个想法是

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

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

如您所见,它并不漂亮,但很有效。

我在这里创建了一个要点

于 2018-06-28T05:10:44.290 回答
1

AFAIK, you can do it only for insert(), update(), and delete(), as it doesn't require specific SQL statement that needs to be verified at compile time.

example:

BaseDao.java

public interface BaseDao<T> {

    @Insert
    void insert(T obj);

    @Insert
    void insert(T... obj);

    @Update
    void update(T obj);

    @Delete
    void delete(T obj);
}

UserDao.java

@Dao
abstract class UserDao implements BaseDao<User> {

    @Query("SELECT * FROM User")
    abstract List<User> getUser();

}

source

于 2018-04-18T16:06:20.650 回答
0

应按如下方式实施:

interface BaseDao<T : BaseEntity> {
    @Insert
    fun insert(entity: T)

    @Insert
    fun insert(vararg entities: T)

    @Update
    fun update(entity: T)

    @Delete
    fun delete(entity: T)

    fun getAll(): LiveData<List<T>>
}



@Dao
abstract class MotorDao : BaseDao<Motor> {

    @Query("select * from Motor")
    abstract override fun getAll(): LiveData<List<Motor>>

}
于 2020-09-09T11:35:33.750 回答
0

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

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

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

于 2017-07-15T15:44:18.710 回答