Java 合并用户';迁移数据库时删除Android Room数据库

Java 合并用户';迁移数据库时删除Android Room数据库,java,android-room,database-migration,Java,Android Room,Database Migration,我有一个房间数据库,它与预加载的数据一起存储在资产/数据库中。我正在为下一次更新创建一个更新版本,其中包含数据库中的更多内容 目前,如果我向数据库添加新内容而不更改模式并重新安装应用程序,则这些新内容不会显示。我能看到更改的唯一方法是卸载并重新安装应用程序。但是,我需要将用户的数据与具有新内容的数据库合并,因为我需要获取用户的“收藏夹”,它是具有项目内容的表的整数列 这可能吗 这就是我创建数据库的方式 公共静态AppDatabase getInMemoryDatabase(上下文){ if(实例

我有一个房间数据库,它与预加载的数据一起存储在资产/数据库中。我正在为下一次更新创建一个更新版本,其中包含数据库中的更多内容

目前,如果我向数据库添加新内容而不更改模式并重新安装应用程序,则这些新内容不会显示。我能看到更改的唯一方法是卸载并重新安装应用程序。但是,我需要将用户的数据与具有新内容的数据库合并,因为我需要获取用户的“收藏夹”,它是具有项目内容的表的整数列

这可能吗

这就是我创建数据库的方式

公共静态AppDatabase getInMemoryDatabase(上下文){
if(实例==null){
已同步(AppDatabase.class){
if(实例==null){
INSTANCE=Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class,“app\u database.db”)
.createFromAsset(“数据库/QuotesDB.db”)
.addMigrations(迁移1\u 2)
.build();
}
}
}
返回实例;
}
我尝试使用以下代码进行迁移,但仍然无法更新内容

/**
*从以下位置迁移:
*第1版-初始内容。
*到
*版本2-更新的数据库内容(无架构更改)
*/
@可视性测试
静态最终迁移\u 1\u 2=新迁移(1,2){
@凌驾
public void migrate(支持sqlitedatabase数据库){
//我需要告诉房间,它应该使用这些数据
//从版本1(带有用户的收藏夹)到版本2。
}
};
这可能吗? 对但是它有点复杂

简而言之,你实际上可以反过来做。而不是使用资产中的新数据库并尝试检索以前的数据(如果使用文件室迁移,则会很复杂,因为您必须有效地交换到新创建/复制的数据库,而迁移时您在事务中,这会更复杂)

但是,如果对正在使用的数据库而不是资产数据库进行模式更改,则可以获取资产数据库并复制新的非用户数据(如果用户数据与非用户数据混合,则会非常复杂)

甚至这也不是那么简单。然而,这里有一个简单的exmaple/scanario,它基于对代码的轻微扩展:-

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase db) {
        final String TAG = "MIGRATE_1_2";
        Log.d(TAG,"Database Version when called is " + db.getVersion());

        // I need to tell Room that it should use the data
        // from version 1 ( with the user's favorites ) to version 2.

        // "CREATE TABLE IF NOT EXISTS `userdata` (`userId` INTEGER DEFAULT uid, `name` TEXT, PRIMARY KEY(`userId`))"
        //db.execSQL("CREATE TABLE IF NOT EXISTS `userdata_saveuserdata` (`userId` INTEGER, `name` TEXT, PRIMARY KEY(`userId`))");
        //db.execSQL("INSERT INTO `userdata_saveuserdata` SELECT * FROM `userdata`");
        db.execSQL("ALTER TABLE `otherdata` ADD COLUMN `column2` TEXT");
        Log.d(TAG,"Checking Context");
        if (sContext != null) {
            applyAssetDB(db);
        } else {
            Log.d(TAG,"Context is null!!!!");
        }
    }
};
如您所见,这通过添加新列更改了otherdata表(而不是users表)

然后检查sContext是否为null

  • 使用有效的上下文,而不是硬编码路径
然后调用applyAssetDB,即:-

private static void applyAssetDB(SupportSQLiteDatabase sdb) {
    String TAG = "APPLYASSETDB";
    String mainDatabaseName = (new File(sdb.getPath()).getName());
    String assetDatabaseName = mainDatabaseName + "_asset";
    String asset_schema = "asset_schema";
    Log.d(TAG,"Attempting application of asset data to database."
                    + "\n\tActual Database = " + mainDatabaseName
                    + "\n\tAsset Database will be " + assetDatabaseName
                    + "\n\tSchema for attached database will be  " + asset_schema
            );
    copyDatabaseFromAssets(AppDatabase.sContext,MainActivity.ASSETNAME,assetDatabaseName);
    /*
    if (sdb.isWriteAheadLoggingEnabled()) {
        setAssetDBToWALMode(sContext.getDatabasePath(assetDatabaseName).getPath());
    }
    Log.d(TAG,"Attempting to ATTACH  asset database " + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
    sdb.execSQL("ATTACH DATABASE '" + sContext.getDatabasePath(assetDatabaseName).getPath() + "' AS " + asset_schema);
    Log.d(TAG,"Attempting INSERTING NEW DATA using\n\t" + "INSERT OR IGNORE INTO `otherdata` SELECT * FROM `otherdata`." + asset_schema);
    sdb.execSQL("INSERT OR IGNORE INTO `otherdata` SELECT * FROM `otherdata`." + asset_schema);
    Log.d(TAG,"Attempting to DETACH " + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
    sdb.execSQL("DETACH DATABASE '" + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
    */

    int insertRows = 0;
    SQLiteDatabase assetdb = SQLiteDatabase.openDatabase(sContext.getDatabasePath(assetDatabaseName).getPath(),null,SQLiteDatabase.OPEN_READONLY);
    Cursor assetCursor = assetdb.query("`otherdata`",null,null,null,null,null,null);
    ContentValues cv = new ContentValues();
    while (assetCursor.moveToNext()) {
        cv.clear();
        for (String c: assetCursor.getColumnNames()) {
            if (assetCursor.getType(assetCursor.getColumnIndex(c)) == Cursor.FIELD_TYPE_BLOB) {
                cv.put(c,assetCursor.getBlob(assetCursor.getColumnIndex(c)));
            } else {
                cv.put(c,assetCursor.getString(assetCursor.getColumnIndex(c)));
            }
        }
        if (sdb.insert("`otherdata`", OnConflictStrategy.IGNORE,cv) > 0 ) insertRows++;
    }
    Log.d(TAG,"Inserted " + insertRows + " from the Asset Database");
    assetCursor.close();

    Log.d(TAG,"Deleting " + sContext.getDatabasePath(assetDatabaseName).getPath());
    if ((new File(sContext.getDatabasePath(assetDatabaseName).getPath())).delete()) {
        Log.d(TAG,"Copied AssetDatabase successfully deleted.");
    } else {
        Log.d(TAG,"Copied Asset Database file not deleted????");
    }
    Log.d(TAG,"Finished");
}
  • 当试图附加从资产复制的数据库时遇到问题时,注释掉了故意留下的代码,因此恢复为使用单独的连接
这将通过copyDatabaseFromAssets方法(如下所示)将数据库从资产复制到实际数据库位置。它从资产数据库中提取所有非用户的数据,并将其插入原始(但根据更改的架构进行更改)数据库中,依赖OnConflictStrategy。忽略仅插入新行。userdata表未被触动,因此用户的数据被重新命名

  • 显然,这不能满足变化的行数
这里是copyDatabaseFromAssets

private static void copyDatabaseFromAssets(Context context, String assetName, String databaseName) {
    String TAG = "COPYDBFROMASSET";
    int bufferSize = 1024 * 4, length = 0, read = 0, written = 0, chunks = 0;
    byte[] buffer = new byte[bufferSize];
    try {
        Log.d(TAG,"Attempting opening asset " + assetName + " as an InputFileStream.");
        InputStream is = context.getAssets().open(assetName);
        Log.d(TAG,"Attempting opening FileOutputStream " + context.getDatabasePath(databaseName).getPath());
        OutputStream os = new FileOutputStream(context.getDatabasePath(databaseName));
        Log.d(TAG,"Initiating copy.");
        while((length = is.read(buffer)) > 0) {
            read += length;
            os.write(buffer,0,length);
            written += length;
            chunks++;
        }
        Log.d(TAG,"Read " + read + "bytes; Wrote " + written + " bytes; in " + chunks);
        Log.d(TAG,"Finalising (Flush and Close output and close input)");
        os.flush();
        os.close();
        is.close();
        Log.d(TAG,"Finalised");

    } catch (IOException e) {
        throw new RuntimeException("Error copying Database from Asset " + e.getMessage());
    }
}
下面是一个示例ActivityMainActivity,它将这一切放在一起(注意,为了方便起见,我使用了allowMainThreadQueries):-

运行时(更改新架构的相关代码并增加版本后),日志中的结果为:-

2019-11-30 10:56:38.768 12944-12944/a.roommigrationwithassets D/MIGRATE_1_2: Database Version when called is 1
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/MIGRATE_1_2: Checking Context
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Attempting application of asset data to database.
        Actual Database = app_database.db
        Asset Database will be app_database.db_asset
        Schema for attached database will be  asset_schema
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Attempting opening asset database/QuotesDB.db as an InputFileStream.
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Attempting opening FileOutputStream /data/user/0/a.roommigrationwithassets/databases/app_database.db_asset
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Initiating copy.
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Read 12288bytes; Wrote 12288 bytes; in 3
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Finalising (Flush and Close output and close input)
2019-11-30 10:56:38.772 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Finalised
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Inserted 3 from the Asset Database
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Deleting /data/user/0/a.roommigrationwithassets/databases/app_database.db_asset
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Copied AssetDatabase successfully deleted.
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Finished
2019-11-30 10:56:38.815 12944-12944/a.roommigrationwithassets D/ONOPEN: Database Version when called is 2
2019-11-30 10:56:38.816 12944-12944/a.roommigrationwithassets D/ONOPEN: Database Version after Super call is 2
2019-11-30 10:56:38.819 12944-12944/a.roommigrationwithassets D/DBINFO: UserData rowcount = 6
        ID = 1 NAME = OU1
        ID = 2 NAME = OU2
        ID = 3 NAME = OU3
        ID = 4 NAME = ADDEDU100
        ID = 5 NAME = ADDEDU200
        ID = 6 NAME = ADDEDU300

    OtherData rowcount = 3
        ID = 1Column1 = OD1
        ID = 2Column1 = OD2
        ID = 3Column1 = OD3
2019-11-30 10:56:38.821 12944-12944/a.roommigrationwithassets D/DBINFO: UserData rowcount = 6
        ID = 1 NAME = OU1
        ID = 2 NAME = OU2
        ID = 3 NAME = OU3
        ID = 4 NAME = ADDEDU100
        ID = 5 NAME = ADDEDU200
        ID = 6 NAME = ADDEDU300

    OtherData rowcount = 3
        ID = 1Column1 = OD1
        ID = 2Column1 = OD2
        ID = 3Column1 = OD3
AppDatabase类的完整代码(注意,其中包括一些冗余代码)是:-

@Database(version=MainActivity.DBVERSION,exportSchema=false,entities={UserData.class,OtherData.class})
抽象类AppDatabase扩展了RoomDatabase{
抽象AllDao AllDao();
静态语境;
静态最终迁移\u 1\u 2=新迁移(1,2){
@凌驾
公共void迁移(SupportSQLiteDatabase db){
最后一个字符串TAG=“MIGRATE_1_2”;
d(标记,“调用时的数据库版本为”+db.getVersion());
//我需要告诉房间,它应该使用这些数据
//从版本1(带有用户的收藏夹)到版本2。
//如果不存在'userdata'('userId'整数默认uid,'name'文本,主键('userId'),则创建表
//db.execSQL(“创建表,如果不存在`userdata\u saveuserdata`(`userId`INTEGER,`name`TEXT,主键(`userId`))”;
//db.execSQL(“插入`userdata\u saveuserdata`SELECT*FROM`userdata`”;
execSQL(“ALTER TABLE`otherdata`ADD COLUMN`column2`TEXT”);
Log.d(标记“检查上下文”);
if(sContext!=null){
applyAssetDB(db);
}否则{
d(标记“Context is null!!!!”);
}
}
};
静态final RoomDatabase.Callback Callback=new RoomDatabase.Callback(){
@凌驾
public void onCreate(@NonNull SupportSQLiteDatabase db){
d(“ONCREATE”,“调用时数据库版本为”+db.getVersion());
super.onCreate(db);
Log.d(“ONCREATE”,“超级调用后的数据库版本为”+db.getVersion());
}
@凌驾
public void onOpen(@NonNull SupportSQLiteDatabase db){
d(“ONOPEN”,“调用时的数据库版本为”+db.getVersion());
super.onOpen(db);
Log.d(“ONOPEN”,“超级调用后的数据库版本为”+db.getVersion());
}
@凌驾
公共voi
2019-11-30 10:56:38.768 12944-12944/a.roommigrationwithassets D/MIGRATE_1_2: Database Version when called is 1
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/MIGRATE_1_2: Checking Context
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Attempting application of asset data to database.
        Actual Database = app_database.db
        Asset Database will be app_database.db_asset
        Schema for attached database will be  asset_schema
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Attempting opening asset database/QuotesDB.db as an InputFileStream.
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Attempting opening FileOutputStream /data/user/0/a.roommigrationwithassets/databases/app_database.db_asset
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Initiating copy.
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Read 12288bytes; Wrote 12288 bytes; in 3
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Finalising (Flush and Close output and close input)
2019-11-30 10:56:38.772 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Finalised
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Inserted 3 from the Asset Database
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Deleting /data/user/0/a.roommigrationwithassets/databases/app_database.db_asset
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Copied AssetDatabase successfully deleted.
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Finished
2019-11-30 10:56:38.815 12944-12944/a.roommigrationwithassets D/ONOPEN: Database Version when called is 2
2019-11-30 10:56:38.816 12944-12944/a.roommigrationwithassets D/ONOPEN: Database Version after Super call is 2
2019-11-30 10:56:38.819 12944-12944/a.roommigrationwithassets D/DBINFO: UserData rowcount = 6
        ID = 1 NAME = OU1
        ID = 2 NAME = OU2
        ID = 3 NAME = OU3
        ID = 4 NAME = ADDEDU100
        ID = 5 NAME = ADDEDU200
        ID = 6 NAME = ADDEDU300

    OtherData rowcount = 3
        ID = 1Column1 = OD1
        ID = 2Column1 = OD2
        ID = 3Column1 = OD3
2019-11-30 10:56:38.821 12944-12944/a.roommigrationwithassets D/DBINFO: UserData rowcount = 6
        ID = 1 NAME = OU1
        ID = 2 NAME = OU2
        ID = 3 NAME = OU3
        ID = 4 NAME = ADDEDU100
        ID = 5 NAME = ADDEDU200
        ID = 6 NAME = ADDEDU300

    OtherData rowcount = 3
        ID = 1Column1 = OD1
        ID = 2Column1 = OD2
        ID = 3Column1 = OD3
@Database(version = MainActivity.DBVERSION, exportSchema = false,entities = {UserData.class,OtherData.class})
abstract class AppDatabase extends RoomDatabase {

    abstract AllDao allDao();
    static Context sContext;

    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase db) {
            final String TAG = "MIGRATE_1_2";
            Log.d(TAG,"Database Version when called is " + db.getVersion());

            // I need to tell Room that it should use the data
            // from version 1 ( with the user's favorites ) to version 2.

            // "CREATE TABLE IF NOT EXISTS `userdata` (`userId` INTEGER DEFAULT uid, `name` TEXT, PRIMARY KEY(`userId`))"
            //db.execSQL("CREATE TABLE IF NOT EXISTS `userdata_saveuserdata` (`userId` INTEGER, `name` TEXT, PRIMARY KEY(`userId`))");
            //db.execSQL("INSERT INTO `userdata_saveuserdata` SELECT * FROM `userdata`");
            db.execSQL("ALTER TABLE `otherdata` ADD COLUMN `column2` TEXT");
            Log.d(TAG,"Checking Context");
            if (sContext != null) {
                applyAssetDB(db);
            } else {
                Log.d(TAG,"Context is null!!!!");
            }
        }
    };

    static final RoomDatabase.Callback CALLBACK = new RoomDatabase.Callback() {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db) {
            Log.d("ONCREATE","Database Version when called is " + db.getVersion());
            super.onCreate(db);
            Log.d("ONCREATE","Database Version after Super call is " + db.getVersion());
        }

        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db) {
            Log.d("ONOPEN","Database Version when called is " + db.getVersion());
            super.onOpen(db);
            Log.d("ONOPEN","Database Version after Super call is " + db.getVersion());
        }

        @Override
        public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
            Log.d("ONDESTRMIG","Database Version when called is " + db.getVersion());
            super.onDestructiveMigration(db);
            Log.d("ONDESTRMIG","Database Version after Super call is " + db.getVersion());
        }
    };

    public void logDBInfo() {
        AllDao adao = this.allDao();
        List<UserData> allUserDataRows = adao.getAllUserDataRows();
        StringBuilder sb = new StringBuilder().append("UserData rowcount = ").append(allUserDataRows.size());
        for (UserData u: allUserDataRows) {
            sb.append("\n\tID = ").append(u.getId()).append(" NAME = " + u.getName());
        }
        List<OtherData> allOtherDataRows = adao.getAllOtherDataRows();
        sb.append("\n\nOtherData rowcount = ").append(allOtherDataRows.size());
        for (OtherData o: allOtherDataRows) {
            sb.append("\n\tID = ").append(o.getOtherDataId()).append("Column1 = ").append(o.getColumn1());
        }
        Log.d("DBINFO",sb.toString());
    }

    static  void setContext(Context context) {
        sContext = context;
    }

    private static void applyAssetDB(SupportSQLiteDatabase sdb) {
        String TAG = "APPLYASSETDB";
        String mainDatabaseName = (new File(sdb.getPath()).getName());
        String assetDatabaseName = mainDatabaseName + "_asset";
        String asset_schema = "asset_schema";
        Log.d(TAG,"Attempting application of asset data to database."
                        + "\n\tActual Database = " + mainDatabaseName
                        + "\n\tAsset Database will be " + assetDatabaseName
                        + "\n\tSchema for attached database will be  " + asset_schema
                );
        copyDatabaseFromAssets(AppDatabase.sContext,MainActivity.ASSETNAME,assetDatabaseName);
        /*
        if (sdb.isWriteAheadLoggingEnabled()) {
            setAssetDBToWALMode(sContext.getDatabasePath(assetDatabaseName).getPath());
        }
        Log.d(TAG,"Attempting to ATTACH  asset database " + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
        sdb.execSQL("ATTACH DATABASE '" + sContext.getDatabasePath(assetDatabaseName).getPath() + "' AS " + asset_schema);
        Log.d(TAG,"Attempting INSERTING NEW DATA using\n\t" + "INSERT OR IGNORE INTO `otherdata` SELECT * FROM `otherdata`." + asset_schema);
        sdb.execSQL("INSERT OR IGNORE INTO `otherdata` SELECT * FROM `otherdata`." + asset_schema);
        Log.d(TAG,"Attempting to DETACH " + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
        sdb.execSQL("DETACH DATABASE '" + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
        */

        int insertRows = 0;
        SQLiteDatabase assetdb = SQLiteDatabase.openDatabase(sContext.getDatabasePath(assetDatabaseName).getPath(),null,SQLiteDatabase.OPEN_READONLY);
        Cursor assetCursor = assetdb.query("`otherdata`",null,null,null,null,null,null);
        ContentValues cv = new ContentValues();
        while (assetCursor.moveToNext()) {
            cv.clear();
            for (String c: assetCursor.getColumnNames()) {
                if (assetCursor.getType(assetCursor.getColumnIndex(c)) == Cursor.FIELD_TYPE_BLOB) {
                    cv.put(c,assetCursor.getBlob(assetCursor.getColumnIndex(c)));
                } else {
                    cv.put(c,assetCursor.getString(assetCursor.getColumnIndex(c)));
                }
            }
            if (sdb.insert("`otherdata`", OnConflictStrategy.IGNORE,cv) > 0 ) insertRows++;
        }
        Log.d(TAG,"Inserted " + insertRows + " from the Asset Database");
        assetCursor.close();

        Log.d(TAG,"Deleting " + sContext.getDatabasePath(assetDatabaseName).getPath());
        if ((new File(sContext.getDatabasePath(assetDatabaseName).getPath())).delete()) {
            Log.d(TAG,"Copied AssetDatabase successfully deleted.");
        } else {
            Log.d(TAG,"Copied Asset Database file not deleted????");
        }
        Log.d(TAG,"Finished");
    }

    private static void copyDatabaseFromAssets(Context context, String assetName, String databaseName) {
        String TAG = "COPYDBFROMASSET";
        int bufferSize = 1024 * 4, length = 0, read = 0, written = 0, chunks = 0;
        byte[] buffer = new byte[bufferSize];
        try {
            Log.d(TAG,"Attempting opening asset " + assetName + " as an InputFileStream.");
            InputStream is = context.getAssets().open(assetName);
            Log.d(TAG,"Attempting opening FileOutputStream " + context.getDatabasePath(databaseName).getPath());
            OutputStream os = new FileOutputStream(context.getDatabasePath(databaseName));
            Log.d(TAG,"Initiating copy.");
            while((length = is.read(buffer)) > 0) {
                read += length;
                os.write(buffer,0,length);
                written += length;
                chunks++;
            }
            Log.d(TAG,"Read " + read + "bytes; Wrote " + written + " bytes; in " + chunks);
            Log.d(TAG,"Finalising (Flush and Close output and close input)");
            os.flush();
            os.close();
            is.close();
            Log.d(TAG,"Finalised");

        } catch (IOException e) {
            throw new RuntimeException("Error copying Database from Asset " + e.getMessage());
        }
    }

    private static void setAssetDBToWALMode(String assetDBPath) {
        SQLiteDatabase db = SQLiteDatabase.openDatabase(assetDBPath,null,SQLiteDatabase.OPEN_READWRITE);
        db.enableWriteAheadLogging();
        db.close();
    }
}
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app_database.db")
                            .createFromAsset("database/QuotesDB.db")
                            .fallbackToDestructiveMigration()
                            .build();
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app_database.db")
       .createFromAsset("database/QuotesDB.db")
       .fallbackToDestructiveMigration()
       .build();