【问题标题】:Upgrade SQLite database from one version to another?将 SQLite 数据库从一个版本升级到另一个版本?
【发布时间】:2011-03-26 08:20:03
【问题描述】:

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

如果这个问题看起来“无聊”,我深表歉意,但我仍在学习 Android。

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

private static final int DATABASE_VERSION = 1;

....

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

...

【问题讨论】:

标签: java android sqlite upgrade


【解决方案1】:

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

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

那么在升级上有什么推荐:

  • 开始交易
  • 使用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)
  • setTransactionSuccessful

.

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();
}

【讨论】:

  • 我可以使用 CREATE TEMPORARY TABLE IF NOT EXISTS 代替 CREATE TABLE IF NOT EXISTS 吗?
  • 不,因为当连接断开时您会丢失它。始终将您的表架构保持为CREATE TABLE IF NOT EXISTS
  • 这如何合并数据库的版本?
  • 每次运行 onUpgrade 方法都会检测到版本更改。您不需要记录数据库的每次更改(如您的问题)。您只需要 latest 表创建模式。我描述的方法使用新表模式创建一个全新的表并传输现有记录。
  • 我不使用这些版本。你不需要。您总是升级到最新版本,您不需要全部升级。拿这个:你有 v1、v10、v50、v80。而您当前定义的版本是 v100。即使从 v1 或 v50 升级,也需要有 v100 的表模式。所以我只是创建 latest (这意味着 v100)表模式,我不关心我从哪个版本升级。我不需要知道它是 v1 还是 v50,它与版本无关。您总是升级到最新版本,无需知道是从哪个版本开始的。
【解决方案2】:

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

在我的应用程序的早期版本中,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);
    }
  }

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

  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();
  }

【讨论】:

    【解决方案3】:

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

    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);
        }
    }
    

    更多信息,请查看blog

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-27
      • 1970-01-01
      • 2020-04-03
      • 2022-09-30
      • 2015-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多