Android Room数据库重启数据丢失,如何在单库创建多表?
问题描述
我是Android X Room Database新手,此前使用Sqlitehelper类操作SQLite数据库,现在迁移到Room。目前遇到的问题是:向Room数据库表插入的数据仅临时存储,重启应用后数据丢失。我希望在单个数据库中创建多张表。
我的DAO代码如下:
@androidx.room.Dao public interface ItineraryDao { @Insert(onConflict = OnConflictStrategy.IGNORE) void insert(ItineraryData model); @Update void update(ItineraryData model); @Delete void delete(ItineraryData model); @Query("DELETE FROM "+ CONSTANTS.ITINERARY_TABLE) void deleteAllItinerary(); @Query("SELECT * FROM "+CONSTANTS.ITINERARY_TABLE+" ORDER BY Date ASC") LiveData<List<ItineraryData>> getAllItinerary(); }
Database代码如下:
@Database(entities = {ItineraryData.class},version = 1,exportSchema = false) public abstract class ItineraryDatabase extends RoomDatabase { private static ItineraryDatabase instance; public abstract ItineraryDao Dao(); public static synchronized ItineraryDatabase getInstance(Context mCon){ if (instance == null) { instance = Room.databaseBuilder(mCon.getApplicationContext(), ItineraryDatabase.class, CONSTANTS.ITINERARY_TABLE) .fallbackToDestructiveMigration() .addCallback(roomCallback) .build(); } return instance; } private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() { @Override public void onCreate(@NonNull SupportSQLiteDatabase db) { super.onCreate(db); new PopulateDbAsyncTask(instance).execute(); } }; private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> { PopulateDbAsyncTask(ItineraryDatabase instance) { ItineraryDao dao = instance.Dao(); } @Override protected Void doInBackground(Void... voids) { return null; } } }
我为每张表使用上述代码生成不同的Dao,为了在单个数据库中创建多张表,我为所有表使用了相同的CONSTANTS.ITINERARY_TABLE。如果使用不同的名称,数据不会丢失,但会创建多个数据库。请问如何在单个数据库中创建多张表且重启应用后不丢失数据?
解决方案
核心问题定位
你把表名当成了数据库文件名传入Room.databaseBuilder,这是导致数据丢失和数据库混乱的根本原因。Room中,databaseBuilder的第三个参数是数据库文件的名称(比如travel_app.db),而不是表名。
1. 统一数据库文件名,创建单个数据库
修改databaseBuilder的第三个参数,使用一个固定的数据库名称常量,比如定义CONSTANTS.APP_DATABASE = "travel_app_db",替换原来的表名参数:
public static synchronized ItineraryDatabase getInstance(Context mCon){ if (instance == null) { instance = Room.databaseBuilder(mCon.getApplicationContext(), ItineraryDatabase.class, CONSTANTS.APP_DATABASE) // 替换为数据库文件名 .fallbackToDestructiveMigration() .addCallback(roomCallback) .build(); } return instance; }
2. 在同一个Database类中声明所有实体(多张表)
Room通过@Database注解的entities数组来管理数据库中的所有表,把你需要创建的所有实体类都添加进去:
// 假设还有UserData、NoteData两个实体类,对应两张表 @Database(entities = {ItineraryData.class, UserData.class, NoteData.class}, version = 1, exportSchema = false) public abstract class ItineraryDatabase extends RoomDatabase { // ... 其他代码 }
3. 在Database类中添加所有DAO的抽象方法
每张表对应一个DAO接口,在Database类中声明对应的抽象方法,方便获取各表的操作实例:
public abstract ItineraryDao itineraryDao(); public abstract UserDao userDao(); public abstract NoteDao noteDao();
4. 排查数据丢失的其他潜在原因
- 检查
fallbackToDestructiveMigration():这个方法会在数据库版本升级且无迁移规则时,销毁旧数据库重建,导致数据丢失。开发阶段可以临时使用,正式发布前必须编写迁移逻辑。 - 确保数据库操作在非主线程执行:Room默认禁止主线程操作数据库,若插入操作在主线程执行会抛出异常(未捕获的话数据不会写入)。推荐用
AsyncTask、Coroutines或RxJava执行数据库操作。 - 验证实体类注解正确性:确保实体类加了
@Entity注解,且tableName与DAO查询中的表名一致:
@Entity(tableName = CONSTANTS.ITINERARY_TABLE) public class ItineraryData { @PrimaryKey(autoGenerate = true) private int id; // 其他字段... }
内容的提问来源于stack exchange,提问作者Supun Ayeshmantha
相关产品推荐
相关产品推荐

