@Entity
class MainTable {
@PrimaryKey
Long id=null;
String name;
public MainTable(){}
public MainTable(Long id, String name) {
this.id=id;
this.name=name;
}
public MainTable(String name) {
this.name=name;
}
}
OtherTable要删除的表
@Entity
class OtherTable {
@PrimaryKey
Long otherId=null;
String otherName;
}
AllDAODAO的接口:-
@Dao
interface AllDAOs {
@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(MainTable mainTable);
/* Not necessary as not used in demo but would likely exist before removal of OtherTable
@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(OtherTable otherTable);
*/
@Query("SELECT * FROM maintable")
List<MainTable> getAllFromMainTable();
/* Not necessary as not used in demo but would likely exist before removal of OtherTable
@Query("SELECT * FROM othertable")
List<OtherTable> getAllFromOtherTable();
*/
}
@Database(
entities = {
MainTable.class
, OtherTable.class /*<<<<<<<<<< WILL BE REMOVED for V15 */
},
exportSchema = false,
version = TheDatabase.DATABASE_VERSION
)
abstract class TheDatabase extends RoomDatabase {
public final static String DATABASE_NAME = "the_database.db";
public final static int DATABASE_VERSION = 14;
abstract AllDAOs getAllDAOs();
private static TheDatabase instance;
public static TheDatabase getInstance(Context context) {
if (instance==null) {
instance = Room.databaseBuilder(context,TheDatabase.class,DATABASE_NAME)
.allowMainThreadQueries()
.addMigrations(mig14to15) /*<<<<<<<<<< The migration can be left in if not invoked */
.build();
}
return instance;
}
private static final Migration mig14to15 = new Migration(14,15) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase supportSQLiteDatabase) {
}
};
}
MainActivity使用数据库的Activity代码
public class MainActivity extends AppCompatActivity {
TheDatabase db;
AllDAOs dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = TheDatabase.getInstance(this);
dao = db.getAllDAOs();
if (TheDatabase.DATABASE_VERSION <= 14) populateMainTable();
for(MainTable mt: dao.getAllFromMainTable()) {
Log.d("DBINFO_V" + TheDatabase.DATABASE_VERSION,"MainTable row is " + mt.name);
}
logSchema(db);
}
private void populateMainTable() {
dao.insert(new MainTable(1000L,"Name1"));
dao.insert(new MainTable("Name2"));
MainTable name3 = new MainTable();
name3.name = "Name3";
dao.insert(name3);
}
@SuppressLint("Range")
private void logSchema(TheDatabase db) {
SupportSQLiteDatabase sdb = db.getOpenHelper().getWritableDatabase();
Cursor csr = db.query(new SimpleSQLiteQuery("SELECT * FROM sqlite_master"));
while (csr.moveToNext()) {
Log.d(
"LOGSCHEMA_V" + TheDatabase.DATABASE_VERSION,
"Component is " + csr.getString(csr.getColumnIndex("name"))
+ " Type is " + csr.getString(csr.getColumnIndex("type"))
);
}
}
}
db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a9dc621848cb6cf03f2ce9a3e93ee987')");
当应用程序运行时,日志包括:
2023-03-31 11:46:28.521 D/DBINFO_V14: MainTable row is Name1
2023-03-31 11:46:28.521 D/DBINFO_V14: MainTable row is Name2
2023-03-31 11:46:28.521 D/DBINFO_V14: MainTable row is Name3
2023-03-31 11:46:28.522 D/LOGSCHEMA_V14: Component is android_metadata Type is table
2023-03-31 11:46:28.522 D/LOGSCHEMA_V14: Component is MainTable Type is table
2023-03-31 11:46:28.522 D/LOGSCHEMA_V14: Component is OtherTable Type is table
2023-03-31 11:46:28.522 D/LOGSCHEMA_V14: Component is room_master_table Type is table
@Database(
entities = {
MainTable.class
/*, OtherTable.class */ /*<<<<<<<<<< WILL BE REMOVED for V15 */
},
exportSchema = false,
version = TheDatabase.DATABASE_VERSION
)
abstract class TheDatabase extends RoomDatabase {
public final static String DATABASE_NAME = "the_database.db";
public final static int DATABASE_VERSION = 14;
abstract AllDAOs getAllDAOs();
private static TheDatabase instance;
public static TheDatabase getInstance(Context context) {
if (instance==null) {
instance = Room.databaseBuilder(context,TheDatabase.class,DATABASE_NAME)
.allowMainThreadQueries()
//.addMigrations(mig14to15) /*<<<<<<<<<< The migration can be left in if not invoked */
.build();
}
return instance;
}
private static final Migration mig14to15 = new Migration(14,15) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase supportSQLiteDatabase) {
}
};
}
instance = Room.databaseBuilder(context,TheDatabase.class,DATABASE_NAME)
.allowMainThreadQueries()
.addMigrations(mig14to15) /*<<<<<<<<<< The migration can be left in if not invoked */
.build();
现在运行:- 日志包括:-
2023-03-31 12:05:37.430 D/DBINFO_V15: MainTable row is Name1
2023-03-31 12:05:37.430 D/DBINFO_V15: MainTable row is Name2
2023-03-31 12:05:37.430 D/DBINFO_V15: MainTable row is Name3
2023-03-31 12:05:37.431 D/LOGSCHEMA_V15: Component is android_metadata Type is table
2023-03-31 12:05:37.431 D/LOGSCHEMA_V15: Component is MainTable Type is table
2023-03-31 12:05:37.431 D/LOGSCHEMA_V15: Component is OtherTable Type is table
2023-03-31 12:05:37.431 D/LOGSCHEMA_V15: Component is room_master_table Type is table
private static final Migration mig14to15 = new Migration(14,15) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase supportSQLiteDatabase) {
supportSQLiteDatabase.execSQL("DROP TABLE IF EXISTS othertable");
}
};
那么最后一步的日志包括:
2023-03-31 12:12:10.955 D/DBINFO_V15: MainTable row is Name1
2023-03-31 12:12:10.955 D/DBINFO_V15: MainTable row is Name2
2023-03-31 12:12:10.955 D/DBINFO_V15: MainTable row is Name3
2023-03-31 12:12:10.956 D/LOGSCHEMA_V15: Component is android_metadata Type is table
2023-03-31 12:12:10.956 D/LOGSCHEMA_V15: Component is MainTable Type is table
2023-03-31 12:12:10.956 D/LOGSCHEMA_V15: Component is room_master_table Type is table
1条答案
按热度按时间4nkexdtk1#
您不能简单地删除迁移。如果方案已更改,则Room会检测到这一点,并且需要迁移,但如果在删除所有表时使用fallbackToDestructiveMigration,则会删除所有数据,然后根据预期的方案创建表,则会出现例外情况。
Room通过将编译时构建的模式的散列与存储在room_master_table中的散列进行比较来检测模式的更改。如果模式已更改,则散列也将更改。
如果散列已更改,则Room将检查版本号是否已更改,如果未更改,则将出现失败,因为需要迁移,但版本号未增加。
如果版本号增加了,那么Room将检查是否有一个Migration包含了从旧版本号到新版本号的更改,除非指定了对
fallbackToDestructiveMigration()
的调用,在这种情况下,Room将调用编译时生成的dropAllTables
方法。如果您要删除实体,并且希望保留数据,则必须进行迁移(如果您希望保留现有数据)。迁移应
DROP
表 (但是,由于Room只关心它所期望的存在,因此如果这是对架构的唯一更改,则可以进行不做任何操作的迁移,并且忽略表)。演示
以下是对上述一些核心方面的简单演示。
首先是@Entity注解的类(2)
MainTable表示要与数据沿着保存的表:-
OtherTable要删除的表
AllDAODAO的接口:-
TheDatabase
@Database
注解类:-MainActivity使用数据库的Activity代码
测试1
当上面被编译时,这将是在删除表之前运行的。作为编译的一部分,如上所述,生成哈希。这个哈希可以在生成的java中看到,例如。
当应用程序运行时,日志包括:
*android_metadata(以及存储locale的android API特定表)
*MainTable和OtherTable按房间
*room_master_table哪个房间用来存储hash。
如果使用应用程序检查,则:-
阶段2删除OtherTable
不是删除类和其他部分,而是删除实体列表以排除OtherTable.class。此外,为了模拟无迁移情况,
.AddMigrations
方法调用被注解掉。因此:编译项目后,生成的java哈希值为
'73c7fb7c7251909ab317f9dbc9309a80'
,与之前的哈希值明显不同。应用程序运行并失败,出现以下情况:-
所以很明显,Room在运行时已经看到模式发生了变化(存储和编译的哈希不匹配)。
阶段3将版本号增加到15
因此代码被更改为使用
public final static int DATABASE_VERSION = 15;
并重新编译。哈希现在是
'73c7fb7c7251909ab317f9dbc9309a80'
,即版本号更改不会更改哈希。所以当应用程序再次运行时,这次又失败了:
因此,需要迁移(请注意,故意不尝试.fallbackToDestructiveMigration(),因为它不会保留数据)。
阶段4通过取消注解
.addMigrations
调用来引入dummy.noop迁移,即:-现在运行:-
日志包括:-
如果应用程序被卸载,然后重复上述步骤,但迁移更改为:-
那么最后一步的日志包括:
即OtherTable已被删除,Room不关心,因为它不是必需的。