首先基于表 B 创建一个表,例如
DROP TABLE IF EXISTS B_temp;
CREATE TABLE B_temp AS SELECT * FROM B;
然后从表 B 中删除所有行,例如
DELETE FROM B;
然后 DROP(或稍后重命名和 DROP)表 A;
DROP TABLE IF EXISTS A;
对表 B 进行更改。
然后使用 B_temp 表中的数据重新加载表 B,例如
INSERT INTO B SELECT * FROM B_temp;
最后删除 B_temp 表;
另一种方法是使用SupportSQliteDatabase
's setForeignKeyConstraintsEnabled
before (false) 和 after (true)。在这种情况下,不会强制执行外键,因此您可以只执行您指定的步骤。
使用方法 1 的示例
以下是基于您问题中的代码的工作示例。
迁移前 - 带有一些数据的版本 1。
@Dao 注解的AllDao抽象类:-
@Dao 抽象类 AllDao {
/* Will be commented out (deleted) for Version 2 */
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(a: A): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(b: B): Long
@Query("SELECT * FROM B")
abstract fun getAllFromB(): List<B>
}
版本 1 和 2的 @Database 注释类TheDatabase :-
const val DATABASE_VERSION = 1
@Database(entities = [A::class,/*<<<<< NOTE will be commented out for Version 2*/B::class], exportSchema = false, version = DATABASE_VERSION)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
companion object {
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
.allowMainThreadQueries()
.addMigrations(MIGRATION_1_2)
.build()
}
return instance as TheDatabase
}
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS B_temp")
db.execSQL("CREATE TABLE IF NOT EXISTS B_temp AS SELECT * FROM B;")
db.execSQL("DELETE FROM B")
db.execSQL("DROP TABLE IF EXISTS A;")
db.execSQL("DROP TABLE IF EXISTS B")
db.execSQL("CREATE TABLE IF NOT EXISTS `B` (`KEY` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `IDKEY` TEXT NOT NULL)")
db.execSQL("INSERT INTO B SELECT * FROM B_temp")
}
}
}
}
一个活动MainActivity,在版本 1 时,将一些数据加载到表 A 和 B 中:-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
@SuppressLint("Range")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
if (DATABASE_VERSION == 1) {
/* Will be commented out for Version 2 */
dao.insert(A("1"))
dao.insert(A("2"))
dao.insert(A("3"))
dao.insert(B(id = 1,parentIDId = "ID001"))
dao.insert(B(id = 2,parentIDId = "ID002"))
dao.insert(B(id = 3,parentIDId = "ID003"))
}
/* Write the schema to the log */
val suppdb = db.openHelper.writableDatabase
val csr = suppdb.query("SELECT * FROM sqlite_master")
while (csr.moveToNext()) {
Log.d("DBINFO",
"Component is ${csr.getString(csr.getColumnIndex("name"))} " +
"Type is ${csr.getString(csr.getColumnIndex("type"))} " +
"SQL is \n\t${csr.getString(csr.getColumnIndex("sql"))}")
}
/* Write the data in table B to the log */
for(b in dao.getAllFromB()) {
Log.d("DBINFO","KEY = ${b.id} IDKEY = ${b.parentIDId}")
}
}
}
结果:-
2022-03-03 08:39:35.353 D/DBINFO: Component is android_metadata Type is table SQL is
CREATE TABLE android_metadata (locale TEXT)
2022-03-03 08:39:35.353 D/DBINFO: Component is A Type is table SQL is
CREATE TABLE `A` (`IDKEY` TEXT NOT NULL, PRIMARY KEY(`IDKEY`))
2022-03-03 08:39:35.353 D/DBINFO: Component is sqlite_autoindex_A_1 Type is index SQL is
null
2022-03-03 08:39:35.354 D/DBINFO: Component is B Type is table SQL is
CREATE TABLE `B` (`KEY` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `IDKEY` TEXT NOT NULL, FOREIGN KEY(`KEY`) REFERENCES `A`(`IDKEY`) ON UPDATE NO ACTION ON DELETE NO ACTION )
2022-03-03 08:39:35.354 D/DBINFO: Component is sqlite_sequence Type is table SQL is
CREATE TABLE sqlite_sequence(name,seq)
2022-03-03 08:39:35.354 D/DBINFO: Component is room_master_table Type is table SQL is
CREATE TABLE room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)
- android_metadata 是由 android SQLite API 创建的表
- 如果任何表包含 AUTOINCREMENT 关键字,则 sqlite_sequence 是创建的表
- room_master_table 是由 Room 创建的,它存储了根据 schema 生成的 hash,用于检测 schema 的变化。
和:-
2022-03-03 08:39:35.361 D/DBINFO: KEY = 1 IDKEY = ID001
2022-03-03 08:39:35.361 D/DBINFO: KEY = 2 IDKEY = ID002
2022-03-03 08:39:35.361 D/DBINFO: KEY = 3 IDKEY = ID003
移民
B类更改为没有将其绑定到表 * A的外键约束:-
@Entity(
tableName = "B"/*,
foreignKeys = [ForeignKey(
entity = A::class,
parentColumns = ["IDKEY"],
childColumns = ["KEY"],
onDelete = NO_ACTION
)]*/)
data class B(
@PrimaryKey(autoGenerate = true)
@NotNull
@ColumnInfo(name = "KEY")
var id: Int = 0,
@ColumnInfo(name = "IDKEY")
var parentIDId: String)
AllDao必须更改为不引用故事 A,所以:-
@Dao
abstract class AllDao {
/* Will be commented out (deleted) for Version 2 */
/*
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(a: A): Long
*/
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(b: B): Long
@Query("SELECT * FROM B")
abstract fun getAllFromB(): List<B>
}
数据库更改为版本 2 并排除表A:-
const val DATABASE_VERSION = 2 /*<<<<<<<<<< changed for version 2 */
@Database(entities = [/*A::class,*//*<<<<< NOTE will be commented out for Version 2*/B::class], exportSchema = false, version = DATABASE_VERSION)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
companion object {
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
.allowMainThreadQueries()
.addMigrations(MIGRATION_1_2)
.build()
}
return instance as TheDatabase
}
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS B_temp")
db.execSQL("CREATE TABLE IF NOT EXISTS B_temp AS SELECT * FROM B;")
db.execSQL("DELETE FROM B")
db.execSQL("DROP TABLE IF EXISTS A;")
db.execSQL("DROP TABLE IF EXISTS B")
db.execSQL("CREATE TABLE IF NOT EXISTS `B` (`KEY` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `IDKEY` TEXT NOT NULL)")
db.execSQL("INSERT INTO B SELECT * FROM B_temp")
}
}
}
}
MainActivity已更改,因此不引用A对象:-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
@SuppressLint("Range")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
if (DATABASE_VERSION == 1) {
/* Will be commented out for Version 2
dao.insert(A("1"))
dao.insert(A("2"))
dao.insert(A("3"))
*/
dao.insert(B(id = 1,parentIDId = "ID001"))
dao.insert(B(id = 2,parentIDId = "ID002"))
dao.insert(B(id = 3,parentIDId = "ID003"))
}
/* Write the schema to the log */
val suppdb = db.openHelper.writableDatabase
val csr = suppdb.query("SELECT * FROM sqlite_master")
while (csr.moveToNext()) {
Log.d("DBINFO",
"Component is ${csr.getString(csr.getColumnIndex("name"))} " +
"Type is ${csr.getString(csr.getColumnIndex("type"))} " +
"SQL is \n\t${csr.getString(csr.getColumnIndex("sql"))}")
}
/* Write the data in table B to the log */
for(b in dao.getAllFromB()) {
Log.d("DBINFO","KEY = ${b.id} IDKEY = ${b.parentIDId}")
}
}
}
结果
运行上述更改后,日志包括:-
2022-03-03 08:53:06.016 D/DBINFO: Component is android_metadata Type is table SQL is
CREATE TABLE android_metadata (locale TEXT)
2022-03-03 08:53:06.016 D/DBINFO: Component is sqlite_sequence Type is table SQL is
CREATE TABLE sqlite_sequence(name,seq)
2022-03-03 08:53:06.016 D/DBINFO: Component is room_master_table Type is table SQL is
CREATE TABLE room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)
2022-03-03 08:53:06.017 D/DBINFO: Component is B_temp Type is table SQL is
CREATE TABLE B_temp("KEY" INT,IDKEY TEXT)
2022-03-03 08:53:06.017 D/DBINFO: Component is B Type is table SQL is
CREATE TABLE `B` (`KEY` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `IDKEY` TEXT NOT NULL)
- 可以看出,没有表A和表B已相应更改(不是外键约束)
并且数据已被保留:-
2022-03-03 08:53:06.023 D/DBINFO: KEY = 1 IDKEY = ID001
2022-03-03 08:53:06.023 D/DBINFO: KEY = 2 IDKEY = ID002
2022-03-03 08:53:06.023 D/DBINFO: KEY = 3 IDKEY = ID003