如何将 Room db 从 1 迁移到 2?
How to migrate Room db from 1 to 2?
我正在尝试将我的 Room 数据库迁移到下一个版本,但我不断收到相同的错误:
java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
我的数据库版本之间的唯一区别是我添加了一个新列。迁移处理如下:
@Database(
version = 2,
entities = [Note::class],
exportSchema = true)
abstract class AppDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDAO
companion object {
fun build(context: Context) = Room.databaseBuilder(context, AppDatabase::class.java, "NotesDatabase")
.addMigrations(MIGRATION_1_2).build()
}
}
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Notes ADD COLUMN image STRING")
}
}
我不确定我是否正确实施了。该错误告诉我必须以某种方式调用 .build()。我在 activity 中尝试使用数据库,但错误是一样的,所以我删除了那个调用。
我该如何解决这个问题?
您似乎没有调用构建函数,可能有另一种构建数据库的方法(调用 databaseBuilder)。
例如在 activity/fragment 你有类似的东西:-
class MainActivity : AppCompatActivity() {
lateinit var db: AppDatabase
lateinit var dao: NoteDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = Room.databaseBuilder(this,AppDatabase::class.java,"NotesDatabase")
.allowMainThreadQueries()
.build()
dao = db.noteDao()
dao.getAllNotes()
}
}
这在更改注释实体并将版本增加到 2 后会产生结果,例如:-
java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
相反,您需要调用 AppDatabase 的构建函数
所以上面会变成:-
class MainActivity : AppCompatActivity() {
lateinit var db: AppDatabase
lateinit var dao: NoteDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = AppDatabase.build(this) //<<<<<<<<<< CHANGED
dao = db.noteDao()
dao.getAllNotes() // Forces database access/open
}
}
HOWEVER,然后您将得到 Expected/Found 类似的问题:-
2022-03-21 09:15:20.385 14533-14533/a.a.so71549033kotlinroommigration E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so71549033kotlinroommigration, PID: 14533
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so71549033kotlinroommigration/a.a.so71549033kotlinroommigration.MainActivity}: java.lang.IllegalStateException: Migration didn't properly handle: notes(a.a.so71549033kotlinroommigration.Note).
Expected:
TableInfo{name='notes', columns={noteText=Column{name='noteText', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, image=Column{name='image', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, noteId=Column{name='noteId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='notes', columns={noteText=Column{name='noteText', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, image=Column{name='image', type='STRING', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='null'}, noteId=Column{name='noteId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
这是因为就空间而言,STRING 不是有效的列类型。
在上面你可以see/extract那个房间需要:-
image=Column{name='image', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}
但是 找到了房间:-
image=Column{name='image', type='STRING', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='null'}
Room 只接受 INTEGER、REAL、TEXT 和 BLOB 列类型(尽管 SQLite 在列类型方面更加灵活)。
类型取决于变量的类型,此外 Room 对定义的其他部分也非常具体,例如 NOT NULL 是否应该是部分以及 DEFAULT 是否应该是定义的一部分或没有。
但是,Room 允许您准确确定列定义应该是什么。如果您对实体进行更改并编译项目,那么 java 代码将由 Room 生成,这包括用于创建 table(s) 的 SQL 以及在此SQL 是预期的列定义。
从 Android Studio 项目视图中,您将看到 Java (generated)
,在此下方的 classes/files 内,将有一些 classes,其中之一将是 AppDatabase_Impl。在这个 class 中会有一个方法 createAlltables。在这个方法中是所有 table 的 SQL。
例如:-
- 注意图像列,在这种情况下,被编码为
var image: String
- String 等同于 TEXT 且 NOT NULL。细绳?不会有 NOT NULL
但是,如果更改 table 并添加一个带有 NOT NULL 的列,那么 SQLite 需要提供一个不为空的 DEFAULT,根据:- f a指定了 NOT NULL 约束,则该列必须具有非 NULL 的默认值。 https://www.sqlite.org/lang_altertable.html
所以在上面的例子中,为了找到房间所期望的,那么 ALTER SQL 应该是:-
ALTER TABLE Notes ADD COLUMN image TEXT NOT NULL DEFAULT 'unknown'
'unknown'
可以是任何合适的
我正在尝试将我的 Room 数据库迁移到下一个版本,但我不断收到相同的错误:
java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
我的数据库版本之间的唯一区别是我添加了一个新列。迁移处理如下:
@Database(
version = 2,
entities = [Note::class],
exportSchema = true)
abstract class AppDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDAO
companion object {
fun build(context: Context) = Room.databaseBuilder(context, AppDatabase::class.java, "NotesDatabase")
.addMigrations(MIGRATION_1_2).build()
}
}
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Notes ADD COLUMN image STRING")
}
}
我不确定我是否正确实施了。该错误告诉我必须以某种方式调用 .build()。我在 activity 中尝试使用数据库,但错误是一样的,所以我删除了那个调用。
我该如何解决这个问题?
您似乎没有调用构建函数,可能有另一种构建数据库的方法(调用 databaseBuilder)。
例如在 activity/fragment 你有类似的东西:-
class MainActivity : AppCompatActivity() {
lateinit var db: AppDatabase
lateinit var dao: NoteDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = Room.databaseBuilder(this,AppDatabase::class.java,"NotesDatabase")
.allowMainThreadQueries()
.build()
dao = db.noteDao()
dao.getAllNotes()
}
}
这在更改注释实体并将版本增加到 2 后会产生结果,例如:-
java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
相反,您需要调用 AppDatabase 的构建函数
所以上面会变成:-
class MainActivity : AppCompatActivity() {
lateinit var db: AppDatabase
lateinit var dao: NoteDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = AppDatabase.build(this) //<<<<<<<<<< CHANGED
dao = db.noteDao()
dao.getAllNotes() // Forces database access/open
}
}
HOWEVER,然后您将得到 Expected/Found 类似的问题:-
2022-03-21 09:15:20.385 14533-14533/a.a.so71549033kotlinroommigration E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so71549033kotlinroommigration, PID: 14533
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so71549033kotlinroommigration/a.a.so71549033kotlinroommigration.MainActivity}: java.lang.IllegalStateException: Migration didn't properly handle: notes(a.a.so71549033kotlinroommigration.Note).
Expected:
TableInfo{name='notes', columns={noteText=Column{name='noteText', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, image=Column{name='image', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, noteId=Column{name='noteId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='notes', columns={noteText=Column{name='noteText', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, image=Column{name='image', type='STRING', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='null'}, noteId=Column{name='noteId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
这是因为就空间而言,STRING 不是有效的列类型。
在上面你可以see/extract那个房间需要:-
image=Column{name='image', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}
但是 找到了房间:-
image=Column{name='image', type='STRING', affinity='1', notNull=false, primaryKeyPosition=0, defaultValue='null'}
Room 只接受 INTEGER、REAL、TEXT 和 BLOB 列类型(尽管 SQLite 在列类型方面更加灵活)。
类型取决于变量的类型,此外 Room 对定义的其他部分也非常具体,例如 NOT NULL 是否应该是部分以及 DEFAULT 是否应该是定义的一部分或没有。
但是,Room 允许您准确确定列定义应该是什么。如果您对实体进行更改并编译项目,那么 java 代码将由 Room 生成,这包括用于创建 table(s) 的 SQL 以及在此SQL 是预期的列定义。
从 Android Studio 项目视图中,您将看到 Java (generated)
,在此下方的 classes/files 内,将有一些 classes,其中之一将是 AppDatabase_Impl。在这个 class 中会有一个方法 createAlltables。在这个方法中是所有 table 的 SQL。
例如:-
- 注意图像列,在这种情况下,被编码为
var image: String
- String 等同于 TEXT 且 NOT NULL。细绳?不会有 NOT NULL
但是,如果更改 table 并添加一个带有 NOT NULL 的列,那么 SQLite 需要提供一个不为空的 DEFAULT,根据:- f a指定了 NOT NULL 约束,则该列必须具有非 NULL 的默认值。 https://www.sqlite.org/lang_altertable.html
所以在上面的例子中,为了找到房间所期望的,那么 ALTER SQL 应该是:-
ALTER TABLE Notes ADD COLUMN image TEXT NOT NULL DEFAULT 'unknown'
'unknown'
可以是任何合适的