Многоразовые базовые классы 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> {
}
Все это
Идея
- Получить имя таблицы универсального типа подкласса во время выполнения
- Передайте это имя таблицы в 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, разрешена, вы можете вставлять, удалять и обновлять пользователя.