Многоразовые базовые классы DAO с Android Room

Есть ли способ создать многоразовые базовые классы DAO с Android Room?

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 это становится очень утомительным...

Ответ 1

Сегодня, 08 августа 2017 года, с версией 1.0.0-alpha8 работает Дао. Я могу иметь другого Дао, играющего в 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 не может быть включен в мой класс базы данных

Ответ 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.

Это не красиво, но работало, как видите.

Я создал суть здесь

Ответ 3

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();

}

источник

Ответ 4

Общая функция findAll:

Базовый репозиторий и дао:

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

пример репо и дао:

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

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

}

@Dao
interface UserDao : BaseDao<User>

Ответ 5

Хотя я согласен с вашим мнением, ответ - нет. По нескольким причинам.

  • Когда fooDao_Impl.java создается из вашего класса @Dao fooDao extends BaseDao<Foo>, вас встретит много ошибок "не удается найти класс Symbol T". Это связано с тем, что метод Room использует для создания dao-реализаций. Это метод, который не будет поддерживать ваш желаемый результат и вряд ли скоро изменится (на мой взгляд, из-за стирания типа).

  • Даже если это разрешено, Room не поддерживает динамические запросы @Dao, чтобы предотвратить SQL-инъекцию. Это означает, что вы можете динамически вставлять значения в запросы, а не имена столбцов, имена таблиц или команды запроса. В приведенном примере вы не можете использовать #{T}, поскольку это нарушит этот принцип. Теоретически, если проблема, описанная в пункте 1, разрешена, вы можете вставлять, удалять и обновлять пользователя.