【问题标题】:How to set a default value to existing rows for a new column created using Room Auto-Migrations?如何为使用 Room Auto-Migrations 创建的新列设置现有行的默认值?
【发布时间】:2022-01-04 06:06:48
【问题描述】:

我有一个包含几列的 Room SQL 表。我想再添加一个包含布尔值的列。为了将我的数据库迁移到这个新模式,我正在尝试使用 Room Auto-Migrations。我的问题是如何更新表中现有的行以使这个新添加的列具有值false?我是否必须通过更改表并将默认值插入所有行来回退到编写手动迁移?

【问题讨论】:

    标签: android sql kotlin android-room database-migration


    【解决方案1】:

    我的问题是如何更新表中现有的行以使这个新添加的列的值为 false?

    使用 @ColumnInfodefalutValue 例如用于实体中的列。

    @ColumnInfo(defaultValue = "0") 
    

    我是否必须通过更改表并将默认值插入所有行来回退到编写手动迁移?

    这应该在自动迁移时添加带有默认值子句的列。

    • 我相信(如果没记错的话)如果您不编写默认值,那么自动迁移将由于新列没有默认值而失败。

    附加

    如果您想使用默认值插入,您将无法在使用@Dao 注释的相应接口/抽象类中使用方便的@Insert 注释,但必须使用@Query 该列表这些列省略了要使用默认值的列,并且具有指定命名列的值的 VALUES 子句。

    例如对于具有 3 列的表,columa、columnb 和 columnc;其中 columnc 指定了 DEFAULT,那么插入函数可能类似于:-

    @Query("INSERT INTO the_table (columna,columnb) VALUES(:valueForColumnA,:valueForColumnB);")
    fun insertWithDefaultValueForColumnC(valueForColumnA: TheType,valueForColumnB: TheType): Long
    
    • 请注意,以上为原则性代码,尚未编译、测试或运行,因此可能包含一些错误。

    补充评论

    我尝试使用@ColumnInfo(defaultValue = "1") 和@ColumnInfo(defaultValue = "true") 将默认值设置为true。但这没有用。

    defaultValue = "1"defaultvalue = "0" 工作 (1 为真 0 为假)

    但是default = "false"default = "true" 不能按预期工作。他们会得到与default = "whatever...." 相似的结果。

    也许是一个错误,也许是 Room 的问题。后者发生的情况是它们作为字符串(SQLite 中的 TEXT)传递,因此即使列已定义为布尔值,它们实际上也是如此插入的。 SQLite 允许这样的值是因为任何列都可以存储任何类型的灵活性。

    考虑以下实体:-

    @Entity
    data class Example1(
        @PrimaryKey
        val id: Long? = null,
        val name: String,
        /*
         ADDITIONS FOR V2
         */
        @ColumnInfo(defaultValue = "1")
        val b1: Boolean,
        @ColumnInfo(defaultValue = "0")
        val b2: Boolean,
        @ColumnInfo(defaultValue = "false")
        val b3: Boolean,
        @ColumnInfo(defaultValue = "true")
        val b4: Boolean
    )
    
    • 添加 b1-b4 并在运行版本 1 时存在 10 行时完成自动迁移,并且在运行版本 2 时运行另外 10 行并添加另外 10 列:-

        db = TheDatabase.getInstance(this)
        dao = db.getAllDao()
        for (i in 1..10) {
            dao.insert(Example1(name = "V${DATABASE_VERSION}_$i", b1 = true, b2 = true, b3 = true, b4 = true))
        }
      
        for(e: Example1 in dao.getAllExample1s()) {
            Log.d("DBINFO","Name is ${e.name} id id ${e.id} " +
            /*" ADDED COL = There Aren't any" */
            " B1=${e.b1} B2=${e.b2} B3=${e.b3} B4=${e.b4}"
            )
        }
      

    第二次运行的结果是:-

    2022-01-05 09:41:43.503 D/DBINFO: Name is V1_1 id id 1  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_2 id id 2  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_3 id id 3  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_4 id id 4  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_5 id id 5  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_6 id id 6  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_7 id id 7  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_8 id id 8  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_9 id id 9  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V1_10 id id 10  B1=true B2=false B3=false B4=false
    2022-01-05 09:41:43.504 D/DBINFO: Name is V2_1 id id 11  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.504 D/DBINFO: Name is V2_2 id id 12  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.505 D/DBINFO: Name is V2_3 id id 13  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.505 D/DBINFO: Name is V2_4 id id 14  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.505 D/DBINFO: Name is V2_5 id id 15  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.505 D/DBINFO: Name is V2_6 id id 16  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.505 D/DBINFO: Name is V2_7 id id 17  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.505 D/DBINFO: Name is V2_8 id id 18  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.506 D/DBINFO: Name is V2_9 id id 19  B1=true B2=true B3=true B4=true
    2022-01-05 09:41:43.506 D/DBINFO: Name is V2_10 id id 20  B1=true B2=true B3=true B4=true
    

    前 10 行已应用默认值:-

    • 根据编码的默认值,b1 为 true,即 1 = true
    • 根据编码的默认值,b2 为 false,即 0 = false
    • b3 和 b4 为 false,因为该值是无法转换为 Int 的 TEXT/STRING(如下所示),因此它不会发生异常,而是转换为 0,因此为 false。

    如果使用 App Inspection 来查看它显示的数据:-

    如果再次使用 App Inspection/Database Inspector 并使用 SELECT *,typeof(b1),typeof(b2),typeof(b3),typeof(b4) FROM example1; 查询的表,则:-

    即自动迁移之前存在的 10 行的 b3 和 b4 列保存为 TEXT。

    【讨论】:

    • 我尝试@ColumnInfo(defaultValue = "1")@ColumnInfo(defaultValue = "true") 将默认值设置为true。但这没有用。
    • @ArpitShukla "1" 和 "0" 按预期工作。根据 SQLite 的规则 (sqlite.org/lang_expr.html#literal_values_constants_),“False”、“True”或任何不被解释为数字的字符串实际上都是错误的。答案已相应更新,以解释您对答案的误用(以及为什么答案没有使用defaulatValue = "false")。 Room 基本上只是将单引号包裹在作为默认值提供的字符串周围,并将其留给 SQLite。它不能防止滥用值(至少对于布尔值),我怀疑任何类型。
    • 我在一个新的虚拟项目中再次尝试了它,并使用0,1,true,false 都在那里工作。虽然当我将默认设置为“hello”时,它确实使列类型为“TEXT”,但除此之外,它在前 4 个默认设置中按预期工作。
    • falsetrue 是否有效取决于 SQLite 版本。如果 3.23.0+(即 Android API 30+),它们会被识别。根据sqlite.org/lang_expr.html#boolean_expressions。 * 但是,不能保证它们作为名为 true 或 false 的对象(列、表等)具有优先权。 0 或 1 保证可以像用作对象名称一样工作,它们必须被括起来。*
    • 知道了。感谢您的回答。
    猜你喜欢
    • 1970-01-01
    • 2011-10-11
    • 2017-08-31
    • 1970-01-01
    • 2021-06-07
    • 2011-05-09
    • 1970-01-01
    • 2013-05-16
    • 2014-06-01
    相关资源
    最近更新 更多