¿Actualizar la base de datos SQLite de una versión a otra?

Recibo un error de Logcat que dice que cierta columna (en mi subclase SQLiteOpenHelper ) no existe. Pensé que podría actualizar la base de datos cambiando la cadena DATABASE_CREATE . Pero aparentemente no, ¿cómo puedo (paso a paso) actualizar mi base de datos SQLite de la versión 1 a la versión 2?

Me disculpo si la pregunta parece “noobish”, pero todavía estoy aprendiendo sobre Android.

@ Pentium10 Esto es lo que hago en onUpgrade:

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

Ok, antes de que te encuentres con problemas mayores deberías saber que SQLite está limitado en el comando ALTER TABLE, permite add y rename solo no remove / drop que se hace con recreación de la tabla.

Siempre debe tener a mano la nueva consulta de creación de tabla, y usarla para actualizar y transferir cualquier información existente. Nota: que los métodos onUpgrade ejecutan uno para su objeto auxiliar sqlite y necesita manejar todas las tablas en él.

Entonces, ¿qué se recomienda en Actualizar?

  • comenzarTransacción
  • ejecutar una creación de tabla con if not exists (estamos haciendo una actualización, por lo que la tabla puede no existir aún, no podrá alterar y soltar)
  • poner en una lista las columnas existentes List columns = DBUtils.GetColumns(db, TableName);
  • tabla de respaldo (tabla ALTER table " + TableName + " RENAME TO 'temp_" + TableName )
  • crear una nueva tabla (el esquema de creación de tabla más nuevo)
  • obtener la intersección con las columnas nuevas, esta vez columnas tomadas de la tabla actualizada ( columns.retainAll(DBUtils.GetColumns(db, TableName)); )
  • restaurar datos ( String cols = StringUtils.join(columns, ","); db.execSQL(String.format( "INSERT INTO %s (%s) SELECT %s from temp_%s", TableName, cols, cols, TableName)); )
  • eliminar la tabla de respaldo ( DROP table 'temp_" + TableName )
  • setTransactionSuccessful

.

 public static List GetColumns(SQLiteDatabase db, String tableName) { List ar = null; Cursor c = null; try { c = db.rawQuery("select * from " + tableName + " limit 1", null); if (c != null) { ar = new ArrayList(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 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(); } 

Así es como actualizo mi base de datos.

En una versión anterior de mi aplicación, la columna gameType no existe. En la nueva versión, lo hace.

  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> values1 = new ArrayList>(); if (c.moveToFirst()) { while (!c.isAfterLast()) { List record = new ArrayList(); 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> values2 = new ArrayList>(); if (c.moveToFirst()) { while (!c.isAfterLast()) { List record = new ArrayList(); 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 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 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); } } 

Aquí está el código para copiar el archivo de la base de datos. La base de datos está inicialmente vacía, y la creé fuera de mi aplicación. (Utilicé un progtwig llamado Navicat para 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(); } 

¿No sería más fácil algo como lo siguiente para la gran mayoría de los casos? Simplemente agregue la nueva columna para cada actualización de versión:

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

Para un poco más sobre esto, mira este blog .