2010-08-06 14 views
13

Viene visualizzato un messaggio di errore da Logcat che indica che una determinata colonna (nella sottoclasse SQLiteOpenHelper) non esiste. Ho pensato di poter aggiornare il database modificando la stringa DATABASE_CREATE. Ma apparentemente no, quindi come posso (passo-passo) aggiornare il mio database SQLite dalla versione 1 alla versione 2?Aggiornare il database SQLite da una versione all'altra?

Mi scuso se la domanda sembra "noobish", ma sto ancora imparando su Android.

@ Pentium10 Questo è quello che faccio nel ONUPGRADE:

private static final int DATABASE_VERSION = 1; 

.... 

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

... 
+0

Vedere anche https://stackoverflow.com/questions/8133597/android-upgrading-db-version-and-adding-new-table – Suragch

risposta

46

Ok, prima di eseguire in problemi più grandi si deve sapere che SQLite è limitata sul comando ALTER TABLE, permette add e rename solo senza rimuovere/drop che viene fatto con la ricreazione del tavolo.

È sempre necessario avere la nuova query di creazione tabella a portata di mano e utilizzarla per aggiornare e trasferire tutti i dati esistenti. Nota: i metodi onUpgrade ne eseguono uno per l'oggetto helper sqlite e devi gestire tutte le tabelle al suo interno.

Quindi, ciò che è raccomandato ONUPGRADE:

  • beginTransaction
  • eseguire un creazione tabella con if not exists (stiamo facendo un aggiornamento, in modo che il tavolo non potrebbe ancora esiste, fallirà alterare e drop)
  • messi in una lista le colonne esistenti List<String> columns = DBUtils.GetColumns(db, TableName);
  • tabella di backup (ALTER table " + TableName + " RENAME TO 'temp_" + TableName)
  • creare nuova tabella (la più recente creazione della tabella s Chema)
  • ottenere l'incrocio con le nuove colonne, questo colonne di tempo presi dalla tabella aggiornata (columns.retainAll(DBUtils.GetColumns(db, TableName));)
  • ripristino dei dati (String cols = StringUtils.join(columns, ","); db.execSQL(String.format( "INSERT INTO %s (%s) SELECT %s from temp_%s", TableName, cols, cols, TableName)); )
  • tabella di backup 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

Potrei aver usato CREATE TEMPORARY TABLE SE NON ESISTE invece di CREATE TABLE SE NON ESISTE ? –

+0

No, poiché lo si perde quando la connessione si interrompe. Mantenere sempre lo schema della tabella come 'CREATE TABLE SE NOT EXISTS' – Pentium10

+0

In che modo incorpora la versione del database? –

1

Ecco come aggiorno il mio database.

In una versione precedente della mia app, la colonna gameType non esiste. Nella nuova versione, lo fa.

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

Ecco il codice per copiare il file di database. Il database è inizialmente vuoto e l'ho creato al di fuori della mia app. (Io ho usato un programma chiamato 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(); 
    } 
1

Non sarebbe qualcosa di simile al seguente essere più facile per la stragrande maggioranza dei casi? Basta aggiungere la nuova colonna per ogni aggiornamento di versione:

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

Per un po 'di più su questo, controlla questo blog.

Problemi correlati