java 将 SQLite 数据库从一个版本升级到另一个版本?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3424156/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 01:47:00  来源:igfitidea点击:

Upgrade SQLite database from one version to another?

javaandroidsqliteupgrade

提问by Mohit Deshpande

I am getting an error from Logcatsaying that a certain column (in my SQLiteOpenHelpersubclass) does not exist. I thought I could upgrade the database by changing the DATABASE_CREATEstring. But apparently not, so how can I (step-by-step) upgrade my SQLite Database from version 1 to version 2?

我收到一个错误,Logcat说某个列(在我的SQLiteOpenHelper子类中)不存在。我以为我可以通过更改DATABASE_CREATE字符串来升级数据库。但显然不是,那么我如何(逐步)将我的 SQLite 数据库从版本 1 升级到版本 2?

I apologize if the question seems "noobish", but I am still learning about Android.

如果问题看起来很“菜鸟”,我深表歉意,但我仍在学习 Android。

@Pentium10 This is what I do in onUpgrade:

@Pentium10 这就是我在 onUpgrade 中所做的:

private static final int DATABASE_VERSION = 1;

....

switch (upgradeVersion) {
case 1:
    db.execSQL("ALTER TABLE task ADD body TEXT");
    upgradeVersion = 2;
    break;
}

...

回答by Pentium10

Ok, before you run into bigger problems you should know that SQLite is limited on the ALTER TABLE command, it allows addand renameonly no remove/drop which is done with recreation of the table.

好的,在遇到更大的问题之前,您应该知道 SQLite 仅限于 ALTER TABLE 命令,它允许add并且rename只允许通过重新创建表完成的删除/删除。

You should always have the new table creation query at hand, and use that for upgrade and transfer any existing data. Note: that the onUpgrade methods runs one for your sqlite helper object and you need to handle all the tables in it.

您应该始终手头有新表创建查询,并将其用于升级和传输任何现有数据。注意: onUpgrade 方法为您的 sqlite helper 对象运行一个,您需要处理其中的所有表。

So what is recommended onUpgrade:

那么什么是推荐 onUpgrade:

  • beginTransaction
  • run a table creation with if not exists(we are doing an upgrade, so the table might not exists yet, it will fail alter and drop)
  • put in a list the existing columns List<String> columns = DBUtils.GetColumns(db, TableName);
  • backup table (ALTER table " + TableName + " RENAME TO 'temp_" + TableName)
  • create new table (the newest table creation schema)
  • get the intersection with the new columns, this time columns taken from the upgraded table (columns.retainAll(DBUtils.GetColumns(db, TableName));)
  • restore data (String cols = StringUtils.join(columns, ","); db.execSQL(String.format( "INSERT INTO %s (%s) SELECT %s from temp_%s", TableName, cols, cols, TableName)); )
  • remove backup table (DROP table 'temp_" + TableName)
  • setTransactionSuccessful
  • 开始交易
  • 运行一个表创建if not exists(我们正在进行升级,所以该表可能还不存在,它会失败并删除)
  • 将现有列放入列表中 List<String> columns = DBUtils.GetColumns(db, TableName);
  • 备份表 ( ALTER table " + TableName + " RENAME TO 'temp_" + TableName)
  • 创建新表(最新的表创建模式)
  • 获取与新列的交集,这次是从升级表 ( columns.retainAll(DBUtils.GetColumns(db, TableName));)
  • 恢复数据 ( String cols = StringUtils.join(columns, ","); db.execSQL(String.format( "INSERT INTO %s (%s) SELECT %s from temp_%s", TableName, cols, cols, TableName)); )
  • 删除备份表 ( DROP table 'temp_" + TableName)
  • 设置交易成功

.

.

public static List<String> GetColumns(SQLiteDatabase db, String tableName) {
    List<String> ar = null;
    Cursor c = null;
    try {
        c = db.rawQuery("select * from " + tableName + " limit 1", null);
        if (c != null) {
            ar = new ArrayList<String>(Arrays.asList(c.getColumnNames()));
        }
    } catch (Exception e) {
        Log.v(tableName, e.getMessage(), e);
        e.printStackTrace();
    } finally {
        if (c != null)
            c.close();
    }
    return ar;
}

public static String join(List<String> list, String delim) {
    StringBuilder buf = new StringBuilder();
    int num = list.size();
    for (int i = 0; i < num; i++) {
        if (i != 0)
            buf.append(delim);
        buf.append((String) list.get(i));
    }
    return buf.toString();
}

回答by BJ Peter DeLaCruz

Here is how I upgrade my database.

这是我升级数据库的方法。

In a previous version of my app, the gameTypecolumn doesn't exist. In the new version, it does.

在我的应用程序的先前版本中,该gameType列不存在。在新版本中,确实如此。

  void upgradeDatabase() throws IOException {
    try {
      String column = DatabaseConstants.GAME_TYPE_COLUMN_NAME; // gameType
      String table = DatabaseConstants.RECORDS_TABLE;
      String query = String.format("SELECT %s FROM %s LIMIT 1", column, table);
      database.rawQuery(query, null);
      return;
    }
    catch (Exception e) {
      // Column doesn't exist. User had old version of app installed, so upgrade database.
    }

    // Save all old data
    String query = "SELECT * FROM " + DatabaseConstants.USERS_TABLE;
    Cursor c = database.rawQuery(query, null);
    List<List<Object>> values1 = new ArrayList<List<Object>>();
    if (c.moveToFirst()) {
      while (!c.isAfterLast()) {
        List<Object> record = new ArrayList<Object>();
        record.add(c.getInt(0));
        record.add(c.getString(1));
        values1.add(record);
        c.moveToNext();
      }
    }
    c.close();

    query = "SELECT * FROM " + DatabaseConstants.RECORDS_TABLE;
    c = database.rawQuery(query, null);
    List<List<Object>> values2 = new ArrayList<List<Object>>();
    if (c.moveToFirst()) {
      while (!c.isAfterLast()) {
        List<Object> record = new ArrayList<Object>();
        record.add(c.getInt(0));
        record.add(c.getInt(1));
        record.add(c.getInt(2));
        record.add(c.getInt(3));
        values2.add(record);
        c.moveToNext();
      }
    }
    c.close();

    // Copy empty database with new schema
    copyDatabase();

    // Restore all old data
    for (List<Object> record : values1) {
      ContentValues cv = new ContentValues();
      cv.put(DatabaseConstants.ID_COLUMN_NAME, (Integer) record.get(0));
      cv.put(DatabaseConstants.USERNAME_COLUMN_NAME, record.get(1).toString());
      database.insert(DatabaseConstants.USERS_TABLE, null, cv);
    }
    for (List<Object> record : values2) {
      ContentValues cv = new ContentValues();
      cv.put(DatabaseConstants.USER_ID_COLUMN_NAME, (Integer) record.get(0));
      cv.put(DatabaseConstants.GAME_TYPE_COLUMN_NAME, GameType.CLASSIC.name());
      cv.put(DatabaseConstants.WINS_COLUMN_NAME, (Integer) record.get(1));
      cv.put(DatabaseConstants.LOSSES_COLUMN_NAME, (Integer) record.get(2));
      cv.put(DatabaseConstants.TIES_COLUMN_NAME, (Integer) record.get(3));
      database.insert(DatabaseConstants.RECORDS_TABLE, null, cv);
    }
  }

Here's the code to copy the database file. The database is initially empty, and I created it outside my app. (I used a program called Navicat for SQLite.)

这是复制数据库文件的代码。数据库最初是空的,我在我的应用程序之外创建了它。(我为 SQLite 使用了一个名为 Navicat 的程序。)

  public DatabaseHelper(Context context) {
    super(context, DatabaseConstants.DATABASE_NAME, null, 1);
    this.context = context;
    databasePath = context.getDatabasePath(DatabaseConstants.DATABASE_NAME).getPath();
  }

  void copyDatabase() throws IOException {
    InputStream is = context.getAssets().open(DatabaseConstants.DATABASE_NAME); // data.db
    OutputStream os = new FileOutputStream(databasePath);

    byte[] buffer = new byte[1024];
    int length;
    while ((length = is.read(buffer)) > 0) {
      os.write(buffer, 0, length);
    }

    // Close the streams.
    os.flush();
    os.close();
    is.close();
  }

回答by PFranchise

Wouldn't something like the following be easier for the vast majority of cases? Just add the new column for each version upgrade:

对于绝大多数情况,以下类似的事情不会更容易吗?只需为每个版本升级添加新列:

private static final String DATABASE_ALTER_TEAM_1 = "ALTER TABLE "
    + TABLE_TEAM + " ADD COLUMN " + COLUMN_COACH + " string;";

private static final String DATABASE_ALTER_TEAM_2 = "ALTER TABLE "
    + TABLE_TEAM + " ADD COLUMN " + COLUMN_STADIUM + " string;";

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (oldVersion < 2) {
         db.execSQL(DATABASE_ALTER_TEAM_1);
    }
    if (oldVersion < 3) {
         db.execSQL(DATABASE_ALTER_TEAM_2);
    }
}

For a bit more on this, check out this blog.

有关更多信息,请查看此博客