【问题标题】:How to handle "migration" in sqlite database如何处理 sqlite 数据库中的“迁移”
【发布时间】:2022-03-17 22:46:19
【问题描述】:

当用户不想更新包含已更新应用的附加列的应用时,我错过了如何处理 SQLite 数据库迁移的明确性。如何处理崩溃以及用户不想更新应用程序时发生的问题。任何建议都将是可观的。

当用户不想更新应用程序时,我尝试了以下代码,其中包括升级数据库版本。

如下升级方法

       @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int 
         newVersion) {
       switch (oldVersion){
        case 1:
            db.execSQL("ALTER TABLE " + TABLE_GITHUB + " ADD COLUMN " + 
                   NODE_ID + " TEXT ");

        case 2:
            db.execSQL("ALTER TABLE " + TABLE_GITHUB + " ADD COLUMN " + 
                   PRIVATE + " TEXT ");

        case 3:
            db.execSQL("ALTER TABLE GITHUB_DATA ADD COLUMN repos_url 
                   text");
    }
}

当用户有SQLite版本2并使用上面的升级方法使其成为3时,我成功升级了SQLite数据库。但是我未能从我添加的列(repos_url 列)中获取数据。该列已添加到 SQLite,但尽管我有来自服务器的数据,但我无法获取数据。任何建议都将是可观的。谢谢。

编辑: 考虑到这种情况如果我使用服务器维护版本控制。

我有 repos_url,我需要为新版本用户包含它。这是我需要在 recyclerview 中显示的适配器。

@Override
public void onBindViewHolder(@NonNull GithubViewHolder githubViewHolder, int i) {

    HashMap<String,String> hashMap = githubArryaList.get(i);

    githubViewHolder.name.setText(hashMap.get("name"));
    githubViewHolder.fullname.setText(hashMap.get("full_name"));
    githubViewHolder.id.setText(hashMap.get("id"));
    githubViewHolder.node_id.setText(hashMap.get("node_id"));
    githubViewHolder.priv.setText(hashMap.get("private"));

    //new column
    githubViewHolder.repos_url.setText(hashMap.get("repos_url"));

}

这是发送数据的sqlite方法。

public ArrayList getFavouritesData(){

    ArrayList arrayList = new ArrayList();
    SQLiteDatabase db = this.getReadableDatabase();
    Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_GITHUB,null);

    if (cursor.moveToFirst()){

        do {

            HashMap<String,String> hashMap = new HashMap<>();
            hashMap.put(SqliteFavouriteDatabase.NAME,cursor.getString(cursor.getColumnIndex(SqliteFavouriteDatabase.NAME)));
            hashMap.put(SqliteFavouriteDatabase.FULL_NAME,cursor.getString(cursor.getColumnIndex(SqliteFavouriteDatabase.FULL_NAME)));
            hashMap.put(SqliteFavouriteDatabase.ID,cursor.getString(cursor.getColumnIndex(SqliteFavouriteDatabase.ID)));
            hashMap.put(SqliteFavouriteDatabase.NODE_ID,cursor.getString(cursor.getColumnIndex(SqliteFavouriteDatabase.NODE_ID)));
            hashMap.put(SqliteFavouriteDatabase.PRIVATE,cursor.getString(cursor.getColumnIndex(SqliteFavouriteDatabase.PRIVATE)));
            hashMap.put(SqliteFavouriteDatabase.REPOS_URL,cursor.getString(cursor.getColumnIndex(SqliteFavouriteDatabase.REPOS_URL)));


            arrayList.add(hashMap);

        }while (cursor.moveToNext());
    }
    cursor.close();
    db.close();
    return arrayList;
}

问题是旧版本如何处理新列数据。当旧版本没有名为 repos_url 的列但我必须为新用户使用 repos_url 并且我必须使用相同的列时,就会出现问题两个版本(新旧)的方法和适配器。如何处理这两个版本。谢谢

【问题讨论】:

  • 您好,当您说“我无法获取数据”时,您的意思是您插入了一个带有repos_url 值的新行,但是当您选择它时它是空的?
  • 如果用户不想更新应用程序,那么您必须为他们的应用程序维护一个单独的 API/应用程序版本。 sqlite 不支持您的用例。
  • @PozzoApps 我已经从服务器获得了 repos_url 的值,同时我已经使用上面的升级方法更新了 SQLite 表列。该表已添加,但当我从 SQLite.db 文件中检查时,repos_url 的值为 null
  • @kkarakk 是的,你是对的,我有同样的想法来维护 api 的版本控制,但如何维护 sqlite 列。我已经在 sqlite db 中添加了 sqlite 列。这不会是一个问题对于不升级应用程序的用户。我们使用向 getData() 发送数据的方法,我添加了新列 (repos_url)。
  • 好的,在这种情况下,您需要接受旧应用版本不会有新列。

标签: android sqlite


【解决方案1】:

这是向后兼容——你的新服务器与旧版本的应用程序不兼容。

虽然修改旧版本的应用程序不可能(或很难),但您可以在新版本的应用程序和新版本的服务器中进行更改以处理此问题:

  1. 在新版本的应用程序中,当向服务器提出请求时,添加额外信息以表明这是来自新版本的应用程序
  2. 在新版本的服务器中,当收到来自应用程序的请求时,检查是否有额外的信息可用
  3. 如果有额外信息,它来自新版本的应用程序,您可以在响应中添加新列
  4. 如果没有额外的信息,它来自旧版本的应用程序,您应该删除响应中的新列
  5. 新版本的应用将接收新的列和句柄
  6. 旧版应用会收到没有新列的响应,并且能够处理。

【讨论】:

  • 是的,您以非常好的方式解释了它。这太棒了,但我只需要在一种情况下澄清一下。我正在使用 recyclerview 发送数据,该数据在适配器中具有名为 repos_url 的新值,用于新版本时会发生什么用户有旧版本,但我仍然在两个版本的适配器中使用这个 repos_url。 @艾敏
  • 你明白我在第 4 步中的意思了吗?服务器不应在对旧版本的响应中包含 repos_url。否则,这取决于您编写旧版本应用程序的好坏——如果只是忽略未知列,您的应用程序仍然存在,否则它将崩溃。
  • "服务器不应在对旧版本的响应中包含 repos_url"..... 是的,但是当我将此变量包含在用于两个版本的适配器中时,这不是问题就像我编辑了我的问题。我必须为新版本添加 repos_url 值。但是我在两个版本中都使用了同一个适配器。此时,旧版本通过从服务器获取旧版本 api 来请求 repos_url 值会发生什么。
【解决方案2】:

通过简单的架构更改进行迁移

1.增加版本:

    public DatabaseHelper(Context c) {
    super(c, DATABASE_HOME, null, 2);
  ...
}

2.添加迁移

        @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion < newVersion) {
    database.execSQL("ALTER TABLE users "
            + " ADD COLUMN last_update INTEGER");
        }
    }

具有复杂架构更改的迁移:

SQLite 的 ALTER TABLE... 命令非常有限。例如,将用户的 id 从 int 更改为 String 需要几个步骤:

使用新架构创建一个新的临时表

将用户表中的数据复制到临时表中

删除用户表

将临时表重命名为 users

     @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            if (oldVersion < newVersion) {

       // Create the new table
        database.execSQL(
                "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))");

     // Copy the data
        database.execSQL(
                "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users");

      // Remove the old table
        database.execSQL("DROP TABLE users");

      // Change the table name to the correct one
        database.execSQL("ALTER TABLE users_new RENAME TO users");
               }

            }
        }

【讨论】:

    猜你喜欢
    • 2016-09-06
    • 2015-05-28
    • 2021-05-23
    • 2015-11-20
    • 2015-09-12
    • 2013-04-09
    • 2017-11-05
    • 2021-09-21
    • 1970-01-01
    相关资源
    最近更新 更多