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

发布于 2024-09-13 08:32:26 字数 429 浏览 5 评论 0原文

我从 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;
}

...

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

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

@Pentium10 This is what I do in onUpgrade:

private static final int DATABASE_VERSION = 1;

....

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

...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

萌逼全场 2024-09-20 08:32:26

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

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

所以建议 onUpgrade:

  • beginTransaction
  • 运行一个表创建,如果不存在(我们正在进行升级,所以表可能还不存在,它将失败更改并删除)
  • 将现有的列表放入列表中列 Listcolumns = DBUtils.GetColumns(db, TableName);
  • 备份表(ALTER table " + TableName + " RENAME TO 'temp_" + TableName
  • 创建新表(最新的表创建架构)
  • 获取与新列的交集,这次是从升级后的表中获取的列 (columns.retainAll(DBUtils.GetColumns(db, TableName));)
  • 恢复数据 (String cols = StringUtils.加入(列,“,”);
    db.execSQL(字符串.format(
    "插入 %s (%s) 从 temp_%s 中选择 %s",
    表名,列,列,表名));
    )
  • 删除备份表 (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();
}

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

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.

So what is recommended 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

.

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();
}
汹涌人海 2024-09-20 08:32:26

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

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

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

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.

知你几分 2024-09-20 08:32:26

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

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

Here is how I upgrade my database.

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

  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.)

  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();
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文