web-dev-qa-db-fra.com

DAO génériques de classe de base réutilisables avec Android Room

Existe-t-il un moyen de créer des DAO de classe de base génériques réutilisables avec 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> { ... }

Je n'ai pas été en mesure de trouver un moyen d'y parvenir sans avoir à déclarer les mêmes membres d'interface et à écrire la requête pour chaque sous-classe. Lorsqu'il s'agit d'un grand nombre de DAO similaires, cela devient très fastidieux ...

20
pqvst

Aujourd'hui, 08 août 2017, avec la version 1.0.0-alpha8 le Dao ci-dessous fonctionne. Je peux avoir d'autres Dao héros du GenericDao.

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

    @Update
    void update(T entity);

    @Delete
    void delete(T entity);
}

Cependant, GenericDao ne peut pas être inclus dans ma classe Database

9
Natan

J'ai une solution pour findAll.

Codes dans ce 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);

et d'autres Dao ressemblent à:

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

C'est tout

L'idée est

  1. Récupère le nom de table du type générique de la sous-classe lors de l'exécution
  2. Passez ce nom de table à une RawQuery

Si vous préférez l'interface à la classe abstraite, vous pouvez essayer la méthode facultative Java 8.

Ce n'est pas beau mais a fonctionné, comme vous pouvez le voir.

j'ai créé un Gist ici

4
Clxy

AFAIK, vous ne pouvez le faire que pour insert(), update() et delete(), car il ne nécessite pas d'instruction SQL spécifique qui doit être vérifiée au moment de la compilation .

exemple:

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

1
Kharda

Fonction findAll générique:

référentiel de base et 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>
}

service de base de données:

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

exemple repo et dao:

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

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

}

@Dao
interface UserDao : BaseDao<User>
0
jc12