在会议室

时间:2018-12-16 01:49:11

标签: sqlite kotlin database-migration android-room annotation-processing

使用Room时,只要有新表添加到数据库中,就必须在迁移中创建它。不幸的是,Room没有仅通过提供类名来创建表的方法。以下是必须具备的条件

room.createTable(User::class)

例如, OrmLite

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

必要性来自仅使用简单的 SQLite 查询创建表的复杂性。当前,您可以在编写{<1> SQLite 脚本

migrate中进行操作
db.execSQL("CREATE TABLE IF NOT EXIST `User` (uid INTEGER NON NULL, PRYMARY KEY (`uid`))")

上面的方法没有问题,但是如果您有例如50个字段,它就会变得复杂而冗长的 SQLite 脚本。显然,您不是自己写的,有两种方法可以让Room自动为您生成创建脚本,以便您复制过去。

  1. 构建应用后,将生成AppDatabase_Impl,并将创建所有必需的表。您可以从那里获取查询
  2. 您在exportSchema = true批注中包含@Database,它将在schemas文件夹中创建Room数据库的versionNumber.json模式。您可以从那里获取创建脚本。

但是,以上两种方法都将要求您在没有任何适当迁移的情况下运行应用程序(因为您不知道正确的查询),并且肯定会崩溃。之后,您将获得正确的查询,可以将其包含在迁移方法中。我认为这不是“专业”的方式。另外,即使您获得了较长的 SQLite 查询,它对 PR 也不友好,并且具有较长的 SQLite 查询不是很好的做法易于调试。

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

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

但是问题是房间注释全部用 @Retention(RetentionPolicy.CLASS)注释,只能在编译时访问。它们在运行时不可用。因此,我所有的findAnnotation方法都将返回null。我当时在考虑在编译时进行创建,但没想到怎么做。

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

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

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

1 个答案:

答案 0 :(得分:0)

Room的当前更新开始,实际上已经有了使用Annotation Processing创建 SQL 查询的方法。使用注释处理,您必须编写一个小型库,在构建它时会为您生成Room查询。

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

Room database migration if only new table is added