android - 如何将旧版本的数据库文件导入新版本?

标签 android database android-sqlite

在我的应用程序中,用户可以导出和导入他的数据库文件以备份数据。我更新了数据库版本并添加了更多新列。现在,用户可能备份版本 1 的旧数据库。 在他尝试导入之后,他将得到类似“(1) 没有这样的列:(以及此处的列名)”之类的信息。 导入后如何在旧数据库上调用 onUpgrade? 我可以在加载的数据库上手动调用 onUpgrade 吗? 我该如何解决? 为了安全起见,我是否需要在更新应用程序后告诉用户再次进行新备份? 有人知道吗?

我如何导入的代码:

// importing database
public boolean importDB(String path , File dbFile) {
    try {
        boolean isValid = checkDbIsValid(dbFile);
            if(!isValid){
                Toast.makeText(context, context.getResources().getString(R.string.errorFile),Toast.LENGTH_LONG).show();
                return false;
            }

        //Last loaded file name will be the auto backup file destination
        int lastIndexOf = path.lastIndexOf('/');
        String loadedFileName = path.substring(lastIndexOf + 1);
        new MySharedPreferences(context).putStringLastFileNameSent(loadedFileName);


        FileInputStream fis = new FileInputStream(path);
        //Destination to:
        String outFileName = DATABASE_DIRECTORY + DbContract.DB_NAME;

        OutputStream output = new FileOutputStream(outFileName);
        byte[] buffer = new byte[1024];
        int length;
        while ((length = fis.read(buffer)) > 0) {
            output.write(buffer, 0, length);
        }
        // Close the streams
        output.flush();
        output.close();
        fis.close();

        Toast.makeText(context, context.getResources().getString(R.string.importDatabaseSucceed),Toast.LENGTH_LONG).show();

        return true;
    } catch (Exception e) {
        Toast.makeText(context,context.getResources().getString(R.string.errorImportDatabase),Toast.LENGTH_LONG).show();
        e.printStackTrace();
        return false;
    }
}

这就是我如何检查数据库文件是否有效:

public static boolean checkDbIsValid(File db) {
        SQLiteDatabase sqlDb;
        Cursor cursor;
        try {   
             sqlDb = SQLiteDatabase.openDatabase (db.getPath(), null, SQLiteDatabase.CONFLICT_NONE);
             cursor = sqlDb.query(true,DbContract.TABLE_DAY, null, null, null, null, null, null, null);
             for( String s : DbContract.ALL_COLUMN_KEYS_IN_TABLE_DAY){
                        cursor.getColumnIndexOrThrow(s);
             }

            //This make sure the user can import old database on new Version of table
            try {
                cursor = sqlDb.query(true,DbContract.TABLE_SETTING,null, null, null, null, null, null, null);
                for( String s : DbContract.ALL_COLUMN_KEYS_IN_TABLE_SETTING_v2){
                    cursor.getColumnIndexOrThrow(s);
                }
                System.out.println("DATABASE LOADED FROM V2");
            }catch (Exception fileNotV1){
                System.out.println("ERROR FILE .. IS NOT V2");
                try {
                    for( String s : DbContract.ALL_COLUMN_KEYS_IN_TABLE_SETTING_v1){
                        cursor.getColumnIndexOrThrow(s);
                    }

                    System.out.println("DATABASE LOADED FROM V1");
                }catch (Exception fileNotV2){
                    System.out.println("ERROR FILE .. IS NOT V1");
                    return false;
                }
            }

            if(DbContract.VERSION<sqlDb.getVersion() ){
                return false;
            }
            sqlDb.close();
            cursor.close();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            return false;
        } catch (SQLiteException e) {
            e.printStackTrace();
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

公共(public)类 DbHelper 扩展了 SQLiteOpenHelper {

    public DbHelper(Context context) {
        super(context, DbContract.DB_NAME, null, DbContract.VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DbContract.CREATE_TABLE_WORKS);
        db.execSQL(DbContract.CREATE_TABLE_SETTING);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion == 1) {
            updateToVersion2(db);
        }
    }

    public void updateToVersion2(SQLiteDatabase db){
        db.execSQL("ALTER TABLE " + DbContract.TABLE_SETTING + " ADD "
                + DbContract.COLUMN1 + " REAL DEFAULT 1");
        db.execSQL("ALTER TABLE " + DbContract.TABLE_SETTING + " ADD "
                + DbContract.COLUMN0 + " REAL DEFAULT 0");
        db.execSQL("ALTER TABLE " + DbContract.TABLE_SETTING + " ADD "
                + DbContract.COLUMN2 + " REAL DEFAULT 0");
    }
  }

最佳答案

如果您实际上是将 db 文件复制到存储或从存储中复制,那么理论上当您调用 getReadableDatabase()getWriteableDatabase() 时,它应该检查数据库版本并完成典型的升级流程,调用 onUpgrade()并为您提供新旧版本号。如果是这种情况,并且您正确地实现了 onUpgrade(),那么事情应该“正常工作”。

如果升级没有自动发生,在打开数据库后,您可以使用 getVersion() 自行检查数据库版本。并执行您喜欢的任何命令以手动升级架构。

编辑

为了让 onUpgrade() 自动运行,您需要使用 DbHelper 类打开数据库。否则,您需要一个 DbHelper 实例,以便您可以调用它的 onUpgrade() 方法并将它传递给您手动打开的 SQLiteDatabase

SQLiteDatabase sqlDb = ...;
DbHelper dbHelper = ...;
dbHelper.onUpgrade(sqlDb, sqlDb.getVersion(), DbContract.VERSION);

我不是特别喜欢你检查数据库文件是否有效的方法。您必须跟踪契约(Contract)中的每个模式版本并检查每个版本;这看起来很笨重。相反,如果您将文件复制到应用程序数据库目录中的临时文件中,使用 DbHelper 打开它(它应该尝试打开它),如果出现任何失败,您可以中止并删除该文件会怎么样?如果成功,您只需重命名文件并重新打开数据库即可。这可能就是我的处理方式。

关于android - 如何将旧版本的数据库文件导入新版本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37041461/

相关文章:

android - 房间 - 数据库 '/data/.../db' 的连接池在 X 秒内无法授予标志为 0x2 的线程的连接

android - 有没有办法知道应用程序是第一次启动?

android - 返回到 fragment 时, fragment 的 onPause() 中的缓存数据为 null

sql - 从经典asp中的数据库查询返回的部分数据

java - 从 SQLite 数据库中检索图像并显示在 ListView 中

java - SQLiteDatabase 仅在不存在时才添加

android - 我是否在每次通话时都使整个屏幕无效?

android - 如何在同一 Activity 中获取两个 MapFragments - Google Map Api

mysql - 按用户分组选择5个连续日期的记录

sql - 通过另一个表组合两个表