Android: aggiornamento della versione di DB e aggiunta di una nuova tabella

Ho già creato tabelle sqlite per la mia app, ma ora voglio aggiungere una nuova tabella al database.

Ho cambiato la versione del DB come di seguito

private static final int DATABASE_VERSION = 2; 

e ha aggiunto una stringa per creare una tabella

 private static final String DATABASE_CREATE_color = "CREATE TABLE IF NOT EXISTS files(color text, incident_id text)"; 

onCreate e onUpgrade come di seguito:

 @Override public void onCreate(SQLiteDatabase database) { database.execSQL(DATABASE_CREATE_incident); database.execSQL(DATABASE_CREATE_audio); database.execSQL(DATABASE_CREATE_video); database.execSQL(DATABASE_CREATE_image); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //drop table and add new tables when version 2 released. db.execSQL(DATABASE_CREATE_color); } 

Ma per qualche ragione la nuova tabella non viene creata. Che cosa sto facendo di sbagliato?

1. Informazioni su onCreate () e onUpgrade ()

onCreate(..) viene chiamato ogni volta che l’app viene installata di recente. onUpgrade viene chiamato ogni volta che l’app viene aggiornata e avviata e la versione del database non è la stessa.

2. Incremento della versione db

Hai bisogno di un costruttore come:

 MyOpenHelper(Context context) { super(context, "dbname", null, 2); // 2 is the database version } 

IMPORTANTE: l’incremento della versione dell’app non è sufficiente per onUpgrade !

3. Non dimenticare i tuoi nuovi utenti!

Non dimenticare di aggiungere

 database.execSQL(DATABASE_CREATE_color); 

al tuo metodo onCreate () o alle app appena installate mancherà la tabella.

4. Come gestire più modifiche al database nel tempo

Quando hai successivi aggiornamenti dell’app, molti dei quali hanno aggiornamenti del database, assicurati di controllare oldVersion :

 onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch(oldVersion) { case 1: db.execSQL(DATABASE_CREATE_color); // we want both updates, so no break statement here... case 2: db.execSQL(DATABASE_CREATE_someothertable); } } 

In questo modo quando un utente si aggiorna dalla versione 1 alla versione 3, ottengono entrambi gli aggiornamenti. Quando un utente si aggiorna dalla versione 2 alla 3, riceve solo l’aggiornamento della versione 3 … Dopo tutto, non è ansible contare sul 100% della base utenti da aggiornare ogni volta che si rilascia un aggiornamento. A volte saltano un aggiornamento o 12 🙂

5. Mantenere sotto controllo i numeri di revisione durante lo sviluppo

E infine … chiamando

 adb uninstall  

disinstalla completamente l’app. Quando si installa di nuovo, si è onCreate colpire onCreate che ti impedisce di continuare a incrementare la versione del database nella stratosfera man mano che sviluppi …

Il tuo codice sembra corretto. Il mio suggerimento è che il database pensi già che sia aggiornato. Se hai eseguito il progetto dopo aver incrementato il numero di versione, ma prima di aggiungere la chiamata execSQL , il database sul tuo dispositivo / emulatore di test potrebbe già credere che sia alla versione 2.

Un modo rapido per verificare ciò sarebbe di cambiare il numero di versione in 3 – se si aggiorna dopo, sai che era solo perché il tuo dispositivo credeva che fosse già aggiornato.

È ansible utilizzare il metodo onUpgrade di onUpgrade . Nel metodo onUpgrade, ottieni oldVersion come uno dei parametri.

In onUpgrade utilizzare un switch e in ognuno dei case utilizzare il numero di versione per tenere traccia della versione corrente del database.

È preferibile eseguire il oldVersion da oldVersion a newVersion , incrementando la version di 1 alla volta e quindi aggiornare il database passo dopo passo. Ciò è molto utile quando qualcuno con la versione 1 del database aggiorna l’app dopo un lungo periodo a una versione che utilizza la versione 7 del database e l’app inizia a bloccarsi a causa di alcune modifiche incompatibili.

Quindi gli aggiornamenti nel database verranno eseguiti in senso passo, coprendo tutti i possibili casi, ovvero incorporando le modifiche nel database eseguite per ogni nuova versione e impedendo in tal modo l’arresto anomalo dell’applicazione.

Per esempio:

 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { case 1: String sql = "ALTER TABLE " + TABLE_SECRET + " ADD COLUMN " + "name_of_column_to_be_added" + " INTEGER"; db.execSQL(sql); break; case 2: String sql = "SOME_QUERY"; db.execSQL(sql); break; } } 

@ La risposta di jkschneider è giusta. Comunque c’è un approccio migliore.

Scrivi le modifiche necessarie in un file sql per ogni aggiornamento come descritto nel link https://riggaroo.co.za/android-sqlite-database-use-onupgrade-correctly/

from_1_to_2.sql

 ALTER TABLE books ADD COLUMN book_rating INTEGER; 

from_2_to_3.sql

 ALTER TABLE books RENAME TO book_information; 

from_3_to_4.sql

 ALTER TABLE book_information ADD COLUMN calculated_pages_times_rating INTEGER; UPDATE book_information SET calculated_pages_times_rating = (book_pages * book_rating) ; 

Questi file .sql verranno eseguiti nel metodo onUpgrade () in base alla versione del database.

DatabaseHelper.java

 public class DatabaseHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 4; private static final String DATABASE_NAME = "database.db"; private static final String TAG = DatabaseHelper.class.getName(); private static DatabaseHelper mInstance = null; private final Context context; private DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); this.context = context; } public static synchronized DatabaseHelper getInstance(Context ctx) { if (mInstance == null) { mInstance = new DatabaseHelper(ctx.getApplicationContext()); } return mInstance; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(BookEntry.SQL_CREATE_BOOK_ENTRY_TABLE); // The rest of your create scripts go here. } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.e(TAG, "Updating table from " + oldVersion + " to " + newVersion); // You will not need to modify this unless you need to do some android specific things. // When upgrading the database, all you need to do is add a file to the assets folder and name it: // from_1_to_2.sql with the version that you are upgrading to as the last version. try { for (int i = oldVersion; i < newVersion; ++i) { String migrationName = String.format("from_%d_to_%d.sql", i, (i + 1)); Log.d(TAG, "Looking for migration file: " + migrationName); readAndExecuteSQLScript(db, context, migrationName); } } catch (Exception exception) { Log.e(TAG, "Exception running upgrade script:", exception); } } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { } private void readAndExecuteSQLScript(SQLiteDatabase db, Context ctx, String fileName) { if (TextUtils.isEmpty(fileName)) { Log.d(TAG, "SQL script file name is empty"); return; } Log.d(TAG, "Script found. Executing..."); AssetManager assetManager = ctx.getAssets(); BufferedReader reader = null; try { InputStream is = assetManager.open(fileName); InputStreamReader isr = new InputStreamReader(is); reader = new BufferedReader(isr); executeSQLScript(db, reader); } catch (IOException e) { Log.e(TAG, "IOException:", e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { Log.e(TAG, "IOException:", e); } } } } private void executeSQLScript(SQLiteDatabase db, BufferedReader reader) throws IOException { String line; StringBuilder statement = new StringBuilder(); while ((line = reader.readLine()) != null) { statement.append(line); statement.append("\n"); if (line.endsWith(";")) { db.execSQL(statement.toString()); statement = new StringBuilder(); } } } } 

Un esempio di progetto è fornito nello stesso link: https://github.com/riggaroo/AndroidDatabaseUpgrades

Gestire le versioni del database è una parte molto importante dello sviluppo dell’applicazione. Suppongo che tu abbia già AppDbHelper di class che estende SQLiteOpenHelper . Quando lo estendi dovrai implementare il metodo onCreate e onUpgrade .

  1. Quando sono chiamati i metodi onCreate e onUpgrade

    • onCreate chiamato quando l’app è stata appena installata.
    • onUpgrade chiamato quando l’app viene aggiornata.
  2. Organizzazione delle versioni del database Gestisco le versioni in un metodo di class. Creare l’implementazione della migrazione dell’interfaccia. Es. Per la prima versione creare la class MigrationV1 , la seconda versione crea MigrationV1ToV2 (queste sono le mie convenzioni di denominazione)

 public interface Migration { void run(SQLiteDatabase db);//create tables, alter tables } 

Esempio di migrazione:

 public class MigrationV1ToV2 implements Migration{ public void run(SQLiteDatabase db){ //create new tables //alter existing tables(add column, add/remove constraint) //etc. } } 
  1. Utilizzo delle classi di migrazione

onCreate : poiché onCreate verrà chiamato quando l’applicazione sarà appena installata, sarà necessario eseguire anche tutte le migrazioni (aggiornamenti della versione del database). Quindi onCreate sarà simile a questo:

 public void onCreate(SQLiteDatabase db){ Migration mV1=new MigrationV1(); //put your first database schema in this class mV1.run(db); Migration mV1ToV2=new MigrationV1ToV2(); mV1ToV2.run(db); //other migration if any } 

onUpgrade : questo metodo verrà chiamato quando l’applicazione è già installata e verrà aggiornata alla nuova versione dell’applicazione. Se l’applicazione contiene modifiche al database, inserire tutte le modifiche del database nella nuova class di migrazione e incrementare la versione del database.

Ad esempio, diciamo che l’utente ha installato un’applicazione che ha la versione 1 del database e ora la versione del database è aggiornata a 2 (tutti gli aggiornamenti dello schema conservati in MigrationV1ToV2 ). Ora, quando l’applicazione viene aggiornata, è necessario aggiornare il database applicando le modifiche dello schema del database in MigrationV1ToV2 questo modo:

 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < 2) { //means old version is 1 Migration migration = new MigrationV1ToV2(); migration.run(db); } if (oldVersion < 3) { //means old version is 2 } } 

Nota: tutti gli aggiornamenti (menzionati in onUpgrade ) nello schema del database devono essere eseguiti su onCreate