如何使用 Android Room 在 DAO class 中正确获取 table 名称

How to properly get table name in DAO class using Android Room

我想知道在 Android 房间查询中使用 table 名称时如何避免硬编码。我在 Kotlin 开发,但老实说在这个问题上 Java 或 Kotlin 并不重要。

让我们看看这个简单的 classes:

DAO接口:

@Dao
interface UserDAO {

    @Query("SELECT * FROM USER")
    fun getAll(): List<User>
}

实体class:

@Entity(tableName = "USER")
class User {
}

可以看到table名字"USER"硬编码在UserDAO的@Query中。如何避免这种情况?如何引用@Entity参数tableName?

我想把所有的名字都放在一个地方。

您可以在任何 Util classes 中创建常量,并且可以在 Entity 和 Dao class 中引用该变量,如下所示:-

假设在 MainActivity 中有

    companion object {
        const val TABLE_NAME: String="Cheese"
    }

实体class

@Entity(tableName = MainActivity.TABLE_NAME)

道class将是

@Query("SELECT * FROM "+MainActivity.TABLE_NAME)

注意:- 这是避免硬编码命名的常用方法,您可以从实体 class 中获取 table 名称(需要探索):) .

目前,对于 Kotlin 和 Java,您可以依赖内置的 SQL 语法高亮显示。因此,当您键入查询内容时,Android Studio 会建议已定义的 table 名称。也许您以某种方式关闭了该功能? Don't forget to inject Android Room SQL Language

总而言之,如果您希望远离硬编码的东西,为什么不定义一个属于 Entity class 的伴生对象?恕我直言,混合数据(层)和 Activity class 不是一个好的概念。数据不知道 activity 之类的东西(或任何 UI 相关的东西)。使用 Utils 听起来像是所有东西都有一个错误,而不是从关注的角度来看。

也许下面的代码可以满足您的要求:

@Entity(tableName = WordPl.TABLE_NAME)
data class WordPl(var id: Int,                      
                  var word: String) {

    companion object {
        const val TABLE_NAME = "word_pl"
    }

}

然后在你的 DAO 中,你可以使用 Kotlin 字符串模板:

@Dao
interface DictionaryDao {

    @Query("Select * from ${WordPl.TABLE_NAME}")
    fun getAllWords(): List<WordPl>

}

你可以在 Java 中做这样的事情:

创建TableAnnotation.java接口文件

/**
 * This annotation class is needed to obtain information about the table from the class at runtime.
 * Needed passing the BaseDao needs to retrieve the table name from the parent class.
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface TableAnnotation {
    String tableName();
}

在你的User.javatableclass中这样做

@TableAnnotation(tableName = User.tableName)
@Entity(tableName = User.tableName)
class User {
protected static final String tableName = "USER";
}

如果你想减少样板代码的数量,你可以在 BaseDao 中集成获取 tableName。 在你的BaseDao.java中你可以这样做

@Dao
public abstract class BaseDao<T> {

    /**
     * Get all objects from the database
     * @return the table objects for the entries
     */
    public List<T> getAllObjects() {
        SimpleSQLiteQuery query = new SimpleSQLiteQuery(
                "select * from " + getTableName() + " order by id"
        );
        return doGetAllObjectsById_(query);
    }
    @RawQuery
    protected abstract List<T> doGetAllObjectsById_(SupportSQLiteQuery query);




    /**
     * Get an object from the database by id
     * @param id
     * @return the table object for the entry
     */
    public T getObjectById(long id) {
        SimpleSQLiteQuery query = new SimpleSQLiteQuery(
                "select * from " + getTableName() + " where id = ?",
                new Object[]{id}
        );
        return doGetObjectById_(query);
    }
    @RawQuery
    protected abstract T doGetObjectById_(SupportSQLiteQuery query);




    /**
     * Get the table name of the parent class.
     * @return
     */
    private String getTableName() {

        Class clazz = (Class)
                ((ParameterizedType) getClass().getSuperclass().getGenericSuperclass())
                        .getActualTypeArguments()[0];

        TableAnnotation tableAnnotation = (TableAnnotation) clazz.getAnnotation(TableAnnotation.class);
        String tableName = tableAnnotation.tableName();
        return tableName;
    }

}

在你的UserDAO.java中,将接口更改为public抽象class并继承BaseDao。您在 BaseDao 中指定的所有标准方法都将被传递。

@Dao
public abstract class UserDAO extends BaseDao<UserDAO>
{

    // Add additional methods here specific to this table.
    // ...
}