【问题标题】:Adding a new column into sqlite produces an error: Exception while computing database live data在 sqlite 中添加新列会产生错误:计算数据库实时数据时出现异常
【发布时间】:2021-05-30 13:59:40
【问题描述】:

我有这个数据类,我在其中添加了一个新值requirePolice

@Entity
data class Crime(@PrimaryKey val id : UUID = UUID.randomUUID(),
                 var title: String ="",
                 var date: Date = Date(),
                 var isSolved: Boolean = false,
                 var requirePolice : Boolean = false, <--- this is the new value
                 var suspect: String = ""){

    //designating a picture location p.317
    val photoFileName
        get() = "IMG_$id.jpg"
}

我将数据库从 2 升级到 3 以将其包含在 SQLite 表中,我还保留了以前的升级代码

@Database(entities = [Crime::class], version = 3)
@TypeConverters(CrimeTypeConverters::class) 
abstract class CrimeDatabase: RoomDatabase(){

    abstract fun crimeDao() : CrimeDao

}

val migration_1_2 = object : Migration(1,2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE Crime ADD COLUMN suspect TEXT NOT NULL DEFAULT ''")
    }
}

val migration_2_3 = object : Migration(2,3){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE Crime ADD COLUMN requirePolice TEXT NOT NULL DEFAULT ''")
    }
}

我以这种方式将升级包含到存储库中

private const val DATABASE_NAME = "crime-database"

class CrimeRepository private constructor(context: Context){

    private val database: CrimeDatabase = Room.databaseBuilder(
        context.applicationContext,
        CrimeDatabase::class.java,
        DATABASE_NAME
    ).addMigrations(migration_1_2)
     .addMigrations(migration_2_3)//adding latest migration 
     .build()
...

我不断收到此错误

E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
    Process: com.bignerdranch.android.criminalintent, PID: 22566
    java.lang.RuntimeException: Exception while computing database live data.
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
     Caused by: java.lang.IllegalStateException: Migration didn't properly handle: Crime(com.bignerdranch.android.criminalintent.Crime).
     Expected:
    TableInfo{name='Crime', columns={date=Column{name='date', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, requirePolice=Column{name='requirePolice', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}, suspect=Column{name='suspect', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, isSolved=Column{name='isSolved', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='Crime', columns={date=Column{name='date', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, requirePolice=Column{name='requirePolice', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''''}, isSolved=Column{name='isSolved', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}, suspect=Column{name='suspect', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
        at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:103)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:177)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:416)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:145)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:106)
        at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:731)
        at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:508)
        at androidx.room.RoomDatabase.query(RoomDatabase.java:551)
        at androidx.room.util.DBUtil.query(DBUtil.java:83)
        at com.bignerdranch.android.criminalintent.database.CrimeDao_Impl$3.call(CrimeDao_Impl.java:158)
        at com.bignerdranch.android.criminalintent.database.CrimeDao_Impl$3.call(CrimeDao_Impl.java:155)
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
            ... 3 more

为什么它给了我这个错误? 如何让它发挥作用?

【问题讨论】:

    标签: android kotlin android-sqlite android-room


    【解决方案1】:

    失败的原因解释在:-

    Expected:
        TableInfo{name='Crime', columns={date=Column{name='date', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, requirePolice=Column{name='requirePolice', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}, suspect=Column{name='suspect', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, isSolved=Column{name='isSolved', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
         Found:
        TableInfo{name='Crime', columns={date=Column{name='date', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, requirePolice=Column{name='requirePolice', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''''}, isSolved=Column{name='isSolved', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}, suspect=Column{name='suspect', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
    

    更具体地说,预期(实体预期)是:-

    requirePolice=Column{name='requirePolice', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}
    

    虽然表(找到的)已更改为:-

    requirePolice=Column{name='requirePolice', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''''}
    

    主要区别在于,ALTER 将列类型设置为 TEXT,而不是 INTEGER(布尔等于 INTEGER)的列类型。

    • 即预期 INTEGER(布尔值)找到 TEXT(字符串)

    因此你需要使用:-

    val migration_2_3 = object : Migration(2,3){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE Crime ADD COLUMN requirePolice INTEGER NOT NULL")
    }
    

    或将实体更改为 (可能不是您想要的):-

    @Entity
    data class Crime(@PrimaryKey val id : UUID = UUID.randomUUID(),
                     var title: String ="",
                     var date: Date = Date(),
                     var isSolved: Boolean = false,
                     var requirePolice : String = "", <--- this is the new value
                     var suspect: String = ""){
    
        //designating a picture location p.317
        val photoFileName
            get() = "IMG_$id.jpg"
    }
    
    • 很可能您要更改的是 ALTER 语句而不是实体。

    如何避免这种预期/发现的差异 - 也就是让 Room 为您完成工作

    您实际上可以通过创建/更改实体然后编译 CTRL+F9 来确定预期的内容,在成功编译后生成底层 java 并且这实际上包含用于创建表的 SQL。

    您可以通过选择 Android View 查看 SQL,找到 java(生成的)文件夹,展开它,然后展开文件夹,然后找到与 @Database 类名称相同的类,后缀为 _Impl ( CrimeDatabase_Impl 在你的情况下)在类中会有一个 createAllTables 方法,它包含所有表的预期 SQL,然后你可以提取/复制 SQL 的定义更改的列并在 ALTER 语句中使用它(如果添加新实体,那么您还拥有整个表的 SQL)。

    • 注意按照警告不要更改课程中的任何内容。

    以下是上述示例(但不适用于您的项目):-

    【讨论】:

      【解决方案2】:

      如果你使用的是sql light db,那么你必须这样迁移db,

      @Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
          
          if (oldVersion < 4) {
              db.execSQL("ALTER TABLE " + TABLE_NOTIFICATION + " ADD COLUMN " + COLUMN_DEEP_LINK + " TEXT ");
          }
      }
      

      【讨论】:

      • 在 Kotlin 中怎么样?
      • @SaherAl-Sous 你能用你创建表格的班级更新你的问题吗?
      • 你的意思是数据库的位置?它在模拟器上...我在 gradle 中添加了位置... kapt { arguments { arg("room.schemaLocation", "$projectDir/schemas/") } }
      • 您使用的是 roomDb 而不是 sql lite。
      • 房间在引擎盖下使用 sqlite...也许我应该更新标签...
      猜你喜欢
      • 2023-03-07
      • 2019-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      • 2017-09-08
      • 1970-01-01
      相关资源
      最近更新 更多