在Room中创建表的方法

Method to create tables in Room

当新的 table 添加到您的数据库时使用 Room 时,您必须在迁移中创建它。不幸的是,Room 没有这样的方法来仅通过提供 class 名称来创建 table。以下内容是必备的

room.createTable(User::class)

例如 OrmLite

中存在类似的方法
TableUtils.createTable(connectionSource, User::class.java)

必要性来自创建 tables 的复杂性,只需简单的 SQLite 查询。目前,你可以做的是在你的 migrate 中编写你的创建 SQLite 脚本

db.execSQL("CREATE TABLE IF NOT EXIST `User` (uid INTEGER NON NULL, PRYMARY KEY (`uid`))")

上面的方法没有问题,但是如果你有例如 50 个字段,它会变得复杂和长 SQLite 脚本。显然不是你自己写的,有两种方法可以让Room自动为你生成Create Script,你直接copy过去即可。

  1. 构建应用程序后,AppDatabase_Impl 将生成,它将包含所有必要的 table 创建内容。你可以从那里得到查询
  2. 您在 @Database 注释中包含 exportSchema = true,它将在您的架构文件夹中创建 versionNumber.json Room 数据库架构。您可以从那里获取创建脚本。

但是,上述两种方法都需要您 运行 应用程序而无需任何适当的迁移(因为您不知道正确的查询)并且它肯定会 崩溃 .之后,您就有了可以包含在迁移方法中的正确查询。我认为这不是 "professional" 的做法。此外,即使在您获得长 SQLite 查询之后,它也不是 PR 友好的,而且拥有长 SQLite 无法轻松调试的查询。

因此,我想采用面向对象的方式在迁移时创建 table。显然,我能想到的唯一方法是使用模型数据 class 并根据模型的每个字段生成查询。应该是这样的

fun createTable(db: SupportSQLiteDatabase, clazz: KClass<*>) {
    val fields = extractColumns(clazz)
    val primaryKeys = fields
            .filter { it.primaryKey }
            .map { it.name }

    val createQuery = "CREATE TABLE IF NOT EXISTS `${clazz.simpleName}` (" +
            fields.joinToString(", ") { "`${it.name}` ${it.type} ${it.nonNull}" } +
            ", PRIMARY KEY (" + primaryKeys.joinToString(",") { "`$it`" } +
            "))"
    db.execSQL(createQuery)
}

fun extractColumns(clazz: KClass<*>): Array<Column>{
    val columns = ArrayList<Column>()
    for (field in clazz.declaredMemberProperties){
        val name = field.findAnnotation<ColumnInfo>()?.name ?: field.name
        val type = getSqlType(field.returnType)
        val nonNull = if (field.returnType.isMarkedNullable) "" else "NON NULL"
        val primaryKey = field.findAnnotation<PrimaryKey>() != null
        columns.add(Column(name, type, nonNull, primaryKey))
    }
    return columns.toTypedArray()
}

但问题是Room Annotations 都是用 @Retention(RetentionPolicy.CLASS) 注释的,只能在编译时访问。它们在 运行 时间内不可用。所以我所有的 findAnnotation 方法都会 return null。我想在编译时创建,但想不出怎么做。

所以,我的问题是有没有什么方法可以在编译期间生成 CREATE 脚本,如果可以,该怎么做?

除了我提到的解决方法之外,是否还有其他创建 tables 的方法不涉及前两种复制粘贴方法?

顺便说一下,我不考虑 fallbackToDestructiveMigration。我的意思是,谁会希望他们的用户丢失所有数据?

截至 Room 的当前更新,实际上可以使用 Annotation Processing 创建 SQL 查询。使用注释处理,您必须编写小型库,在构建时为您生成 Room 查询。

创建 Annotation Processing Library 并不简单,这是相关问题。