【发布时间】:2022-03-14 07:07:22
【问题描述】:
假设我这样声明我的实体:
@Entity(tableName = "banana")
data class MyStuff(
@PrimaryKey val id: String
)
当 Room KAPT 执行时,它很神奇,@Entity 注释被剥离,因此它在运行时不再存在。
例如如果我写 MyStuff::class.annotations 它会返回空列表。
如果我使用了@Entity(tableName = "banana"),所以我的表名与我的类名不匹配,我如何查询 Room 数据库以找出给定的实体类型表示给定的表名?
更新
所以我找到了一个解决方法,虽然很可怕。
看来 Room 实际上并不知道 Entity 和 Table 之间的映射。 这些信息被编译到 DAO 的 BODY 中。
这也是为什么他们让你对你所有的表都使用 DAO,这非常冗长。
尽管如此,通过其他一些需求,我的项目中有以下注释:
@Target(AnnotationTarget.CLASS)
annotation class DaoType(val type: KClass<*>)
我还有一个从KClass<*> 到getDao 的方法。
我所有的 DAO 对象都包含一个自动生成的 deleteAll 方法,如下所示:
@Delete
fun deleteAll(vararg entities: MyStuff)
鉴于此,我可以像这样获得表名:
var tableName: String? = null
val daoType = (entityType.annotations.firstOrNull { it is DaoType } as? DaoType)
?.type
daoType?.let {
val dao = getDao<Any>(it.java)
dao?.let { d ->
val statementFields: List<Field> =
d::class.java.declaredFields.filter { f ->
return@filter f.type.declaredMethods.firstOrNull { m -> m.name == "createQuery" } != null
}
for (field in statementFields) {
field.isAccessible = true
val fieldVal = field.get(d) ?: continue
val method = field.type.declaredMethods.first { m -> m.name == "createQuery" }
val query = method.invoke(fieldVal) as? String ?: continue
if (query.startsWith("DELETE FROM ")) {
var matchResult = "DELETE\\sFROM\\s`(.*?)`.*".toRegex().find(query)
val table = matchResult?.groupValues?.lastOrNull()
if (table != null) {
tableName = table
}
}
}
}
}
注意:我没有将此标记为可接受的答案,因为我对 Room 内部工作的依赖程度不满意。
不过,这至少现在有效。
【问题讨论】:
-
只是想知道,为什么你想在运行时知道你的表名?
-
@SwapnilPadaya 我在数据库中有 100 多个表。我有共同的任务,例如“计算表中的行数”或“删除表中的所有行”等。我不想在 DAO 中重复这些方法。 (我们首先需要 DAO 已经够糟糕了)。
-
你可以通过 DAO 传递一个参数(例如表名),并像 ContentResolver.query() 通过传递表名、选择投影......
-
Room 在后台使用 Sqlite,因此您应该能够使用此答案中的一种方法stackoverflow.com/questions/15383847/…
-
@IvanWooll 我当然可以得到表名。我如何知道该表对应于哪个 Kotlin 类?
标签: android kotlin android-room