web-dev-qa-db-fra.com

Android: mise à niveau de la version de la base de données et ajout d'un nouveau tableau

J'ai déjà créé des tables sqlite pour mon application, mais je souhaite maintenant ajouter une nouvelle table à la base de données.

J'ai changé la version de la base de données comme ci-dessous

private static final int DATABASE_VERSION = 2;

et Ajout d'une chaîne pour créer un tableau

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

onCreate et onUpgrade comme ci-dessous:

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

    }

Mais pour une raison quelconque, la nouvelle table n'est pas en cours de création. Qu'est-ce que je fais mal?

114
Jay Mayu

1. À propos de onCreate () et onUpgrade ()

onCreate(..) est appelée chaque fois que l'application est fraîchement installée. onUpgrade est appelée à chaque fois que l'application est mise à niveau et lancée et que la version de la base de données n'est pas la même.

2. Incrémentation de la version de la base de données

Vous avez besoin d'un constructeur comme:

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

IMPORTANT: L'incrémentation de la version de l'application ne suffit pas pour que onUpgrade soit appelé!

. N'oubliez pas vos nouveaux utilisateurs!

N'oubliez pas d'ajouter

database.execSQL(DATABASE_CREATE_color);

votre table onCreate () ou les applications nouvellement installées n'auront pas la table.

4. Comment gérer plusieurs changements de base de données au fil du temps

Lorsque vous effectuez des mises à niveau d'applications successives, dont plusieurs avec des mises à niveau de base de données, vous voulez vous assurer de vérifier 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); 
   }
}

Ainsi, lorsqu'un utilisateur effectue une mise à niveau de la version 1 à la version 3, il obtient les deux mises à jour. Lorsqu'un utilisateur effectue une mise à niveau de la version 2 à la version 3, il ne reçoit que la mise à jour de la révision 3 ... Après tout, vous ne pouvez pas compter sur 100% de votre base d'utilisateurs pour la mise à niveau à chaque fois que vous publiez une mise à jour. Parfois, ils sautent une mise à jour ou 12 :)

5. Garder vos numéros de révision sous contrôle pendant le développement

Et enfin ... appeler

adb uninstall <yourpackagename>

désinstaller totalement l'application. Lorsque vous effectuez une nouvelle installation, vous êtes assuré de toucher onCreate, ce qui vous évite de devoir incrémenter continuellement la version de la base de données dans la stratosphère au fur et à mesure de votre développement ...

272
jkschneider

Votre code semble correct. Ma suggestion est que la base de données pense déjà qu'elle a été mise à niveau. Si vous avez exécuté le projet après avoir incrémenté le numéro de version, mais avant d'ajouter l'appel execSQL, la base de données de votre périphérique/émulateur de test peut déjà croire qu'elle est à la version 2.

Un moyen rapide de vérifier cela consiste à changer le numéro de version en 3 - si la mise à niveau est effectuée après cela, vous savez que c'est simplement parce que votre appareil pensait qu'il avait déjà été mis à niveau.

9
Greyson

Vous pouvez utiliser la méthode onUpgrade de SQLiteOpenHelper. Dans la méthode onUpgrade, vous obtenez oldVersion comme l'un des paramètres.

Dans le onUpgrade, utilisez un switch et dans chacun des cases, utilisez le numéro de version pour suivre la version actuelle de la base de données.

Il est préférable de passer de oldVersion à newVersion, en incrémentant version de 1 à la fois, puis de mettre à niveau la base de données étape par étape. Ceci est très utile lorsque quelqu'un avec la version 1 de la base de données met à niveau l'application après une longue période, vers une version utilisant la version 7 de la base de données et que l'application se bloque en raison de certaines modifications incompatibles.

Ensuite, les mises à jour de la base de données seront effectuées étape par étape, en couvrant tous les cas possibles, c'est-à-dire en incorporant les modifications apportées à la base de données pour chaque nouvelle version et en évitant ainsi le blocage de votre application.

Par exemple:

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

}
2
Vijesh Jat

La réponse de @ jkschneider est juste. Cependant, il existe une meilleure approche.

Ecrivez les modifications nécessaires dans un fichier SQL pour chaque mise à jour, comme indiqué dans le lien https://riggaroo.co.za/Android-sqlite-database-use-onupgrade-correctly/

de_1_à_2.sql

ALTER TABLE books ADD COLUMN book_rating INTEGER;

from_2_to_3.sql

ALTER TABLE books RENAME TO book_information;

de_3_à_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) ;

Ces fichiers .sql seront exécutés dans la méthode onUpgrade () en fonction de la version de la base de données.

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 exemple de projet est également fourni dans le même lien: https://github.com/riggaroo/AndroidDatabaseUpgrades

1
oiyio

La gestion des versions de base de données est une partie très importante du développement d’applications. Je suppose que vous avez déjà la classe AppDbHelper qui étend SQLiteOpenHelper. Lorsque vous l'étendez, vous devrez implémenter la méthode onCreate et onUpgrade.

  1. Quand les méthodes onCreate et onUpgrade sont appelées

    • onCreate appelé lors de la dernière installation de l'application.
    • onUpgrade appelé lors de la mise à jour de l'application.
  2. Organisation des versions de la base de données Je gère les versions dans une méthode de classe. Créer une implémentation de l'interface Migration. Par exemple. Pour la première version, créez MigrationV1 classe, deuxième version créer MigrationV1ToV2 _ (ce sont mes conventions de nommage)


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

Exemple de migration:

public class MigrationV1ToV2 implements Migration{
      public void run(SQLiteDatabase db){
        //create new tables
        //alter existing tables(add column, add/remove constraint)
        //etc.
     }
   }
  1. Utiliser les classes de migration

onCreate: Puisque onCreate sera appelé lors de la dernière installation de l'application, nous devons également exécuter toutes les migrations (mises à jour de la version de la base de données). Donc, onCreate ressemblera à ceci:

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: Cette méthode sera appelée lorsque l'application est déjà installée et mise à jour vers la nouvelle version de l'application. Si l'application contient des modifications de base de données, insérez toutes les modifications dans la nouvelle classe de migration et incrémentez la version de la base de données.

Par exemple, supposons que l’utilisateur ait installé une application disposant de la version 1 de la base de données et que celle-ci est mise à jour à la version 2 (toutes les mises à jour de schéma conservées dans MigrationV1ToV2). Maintenant, lorsque l'application est mise à niveau, nous devons mettre à niveau la base de données en appliquant les modifications de schéma de base de données dans MigrationV1ToV2 comme ça:

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

Remarque: Toutes les mises à niveau (mentionnées dans onUpgrade) du schéma de base de données doivent être exécutées dans onCreate

1
VIjay J