2010-08-06 12 views
13

Recibo un error de Logcat diciendo que una 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?¿Actualizar la base de datos SQLite de una versión a otra?

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

... 
+0

Ver también https://stackoverflow.com/questions/8133597/android-upgrading-db-version-and-adding-newtable – Suragch

Respuesta

46

Ok, antes de ejecutar en problemas mayores debe saber que SQLite es limitado en el comando ALTER TABLE, que permite add y rename única sin quitar/drop que se hace con recreación de la mesa.

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.

Así que lo que se recomienda ONUPGRADE:

  • beginTransaction
  • ejecutar una creación de la tabla con if not exists (que estamos haciendo una actualización, por lo que la mesa podría no existe, sin embargo, se producirá un error alterar y gota)
  • puesto en una lista de las columnas existentes List<String> columns = DBUtils.GetColumns(db, TableName);
  • mesa de copia de seguridad (ALTER table " + TableName + " RENAME TO 'temp_" + TableName)
  • crear nueva tabla (la más reciente creación de la tabla s chema)
  • llegar a la intersección con las nuevas columnas, esta vez las columnas tomadas de la mesa superior (columns.retainAll(DBUtils.GetColumns(db, TableName));)
  • restaurar los datos (String cols = StringUtils.join(columns, ","); db.execSQL(String.format( "INSERT INTO %s (%s) SELECT %s from temp_%s", TableName, cols, cols, TableName)); )
  • mesa de copia de seguridad remove (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(); 
} 
+0

¿Podría haber usado CREATE TEMPORARY TABLE IF NOT EXISTS en lugar de CREATE TABLE IF NOT EXISTS? ? –

+0

No, ya que lo perderá cuando se desconecte la conexión. Mantenga siempre el esquema de la tabla como 'CREATE TABLE IF NOT EXISTS ' – Pentium10

+0

¿Cómo se incorpora esto a la versión de la base de datos? –

1

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

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. (He utilizado un programa 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(); 
    } 
1

No sería algo como lo siguiente será más fácil para la gran mayoría de los casos? Sólo tiene que añadir la nueva columna para cada actualización de la 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); 
    } 
} 

Por un poco más sobre esto, echa un vistazo a este blog.

Cuestiones relacionadas