【问题标题】:How to properly get table name in DAO class using Android Room如何使用 Android Room 在 DAO 类中正确获取表名
【发布时间】:2025-12-12 00:15:01
【问题描述】:

我想知道在 Android Room 查询中使用表名时如何避免硬编码。我在Kotlin 开发,但老实说,在这个问题上,Java 或 Kotlin 都没有关系。

让我们看看那些简单的类:

DAO 接口:

@Dao
interface UserDAO {

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

实体类:

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

您可以在 UserDAO 的 @Query 中看到表名“USER”是硬编码的。如何避免这种情况?如何引用@Entity参数tableName

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

【问题讨论】:

  • 您是否尝试过使用 User::class.simpleName?
  • 是的。结果中出现错误:Type mismatch. Required: String Found: String?

标签: android kotlin android-room


【解决方案1】:

你可以在 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.java 表类中执行此操作

@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 中,将接口更改为公共抽象类并继承 BaseDao。您在 BaseDao 中指定的所有标准方法都将被传递。

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

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

【讨论】:

    【解决方案2】:

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

    总而言之,如果您更愿意保留硬编码的东西,为什么不定义一个属于 Entity 类的伴生对象呢?恕我直言,混合数据(层)和活动类不是一个好的概念。数据不知道诸如活动之类的东西(或任何与 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>
    
    }
    

    【讨论】:

      【解决方案3】:

      您可以在任何 Util 类中创建一个常量,并可以像这样在 Entity 和 Dao 类中引用该变量:-

      假设在 MainActivity 你有

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

      实体类

      @Entity(tableName = MainActivity.TABLE_NAME)
      

      道类将是

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

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

      【讨论】:

      • 这个答案是对的,但值得注意的是,数据库中的每个表都应该有单独的类和常量。此外,常量类的名称应参考数据库表名。应避免将常量名称放在MainActivity 类中。