Обновить базу данных SQLite с одной версии на другую? - PullRequest
14 голосов
/ 06 августа 2010

Я получаю ошибку от 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;
}

...

Ответы [ 3 ]

47 голосов
/ 06 августа 2010

Хорошо, перед тем, как столкнуться с более серьезными проблемами, вы должны знать, что SQLite ограничен командой ALTER TABLE, она позволяет add и rename только удаление / удаление, что делается при воссоздании таблицы.

Вы всегда должны иметь под рукой новый запрос на создание таблицы и использовать его для обновления и передачи любых существующих данных.Обратите внимание: что методы onUpgrade запускают один для вашего вспомогательного объекта sqlite, и вам необходимо обработать все таблицы в нем.

Итак, что рекомендуется для onUpgrade:

  • beginTransaction
  • запустить создание таблицы с помощью 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();
}
1 голос
/ 07 мая 2015

Разве что-то подобное не будет легче в подавляющем большинстве случаев? Просто добавьте новый столбец для каждого обновления версии:

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

Подробнее об этом читайте в этом блоге .

1 голос
/ 21 мая 2014

Вот как я могу обновить свою базу данных.

В предыдущей версии моего приложения столбец 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 для 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();
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...