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

时间:2017-06-25 12:08:36

标签: 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> { ... }

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

6 个答案:

答案 0 :(得分: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);
}

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

答案 1 :(得分:2)

我有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的可选方法。

如您所见,它并不漂亮,但可以工作。

I created a gist at here

答案 2 :(得分:1)

AFAIK,您只能对insert()update()delete()执行此操作,因为它不需要在编译时需要验证的特定SQL语句。

示例:

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

答案 3 :(得分:0)

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

示例回购和dao:

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

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

}

@Dao
interface UserDao : BaseDao<User>

答案 4 :(得分: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>>

}

答案 5 :(得分:-1)

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

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

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