Android: обновление версии БД и добавление новой таблицы

101

Я уже создал таблицы sqlite для своего приложения, но теперь хочу добавить новую таблицу в базу данных.

Я изменил версию DB ниже

private static final int DATABASE_VERSION = 2;

и добавлена ​​строка для создания таблицы

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

onCreate и onUpgrade, как показано ниже:

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

    }

Но по какой-то причине новая таблица не создается. Что я делаю неправильно?

  • 0
    Это еще одно интересное решение, но пока самая надежная версия, которую я видел, здесь .
Теги:
database

4 ответа

225
Лучший ответ

1. О onCreate() и onUpgrade()

onCreate(..) вызывается всякий раз, когда приложение только что установлено. onUpgrade вызывается всякий раз, когда приложение обновляется и запускается, а версия базы данных не одинакова.

2. Увеличение версии db

Вам нужен конструктор типа:

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

ВАЖНО: для того, чтобы onUpgrade был вызван! Приостанавливать версию приложения недостаточно, чтобы быть вызванным!

3. Не забудьте новых пользователей!

Не забудьте добавить

database.execSQL(DATABASE_CREATE_color);

к вашему методу onCreate(), а также к новым установленным приложениям будет отсутствовать таблица.

4. Как справляться с несколькими изменениями базы данных с течением времени

Когда у вас есть последовательные обновления приложений, некоторые из которых имеют обновления базы данных, вы обязательно должны проверить 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); 
   }
}

Таким образом, когда пользователь обновляется с версии 1 до версии 3, они получают оба обновления. Когда пользователь обновляется с версии 2 до 3, они просто получают обновление версии 3... В конце концов, вы не можете рассчитывать на 100% своей базы пользователей для обновления при каждом выпуске обновления. Иногда они пропускают обновление или 12:)

5. Ведение номеров ревизий под контролем при разработке

И наконец... вызов

adb uninstall <yourpackagename>

полностью удаляет приложение. Когда вы снова установите, вам гарантированно попадет onCreate, из-за чего вам не придется увеличивать версию базы данных в стратосфере по мере развития...

  • 7
    Хорошая мысль о добавлении вызова onCreate()
  • 5
    Относительно # 4: не лучше ли будет использовать oldVersion аргумент oldVersion ? Если какие-либо инструкции по обновлению повторяются, вы можете в конечном итоге повторить их в самой современной базе данных. Если одним из утверждений является усечение таблицы, это было бы очень плохо.
Показать ещё 21 комментарий
9

Ваш код выглядит правильно. Мое предложение состоит в том, что база данных уже думает, что она обновлена. Если вы выполнили проект после увеличения номера версии, но перед добавлением вызова execSQL база данных вашего тестового устройства/эмулятора может уже поверить в версию 2.

Быстрый способ проверить это - изменить номер версии на 3 - если после этого она обновится, вы знаете, что это было только потому, что ваше устройство считало, что оно уже обновлено.

  • 1
    Благодарю. Дат помог :) После смены на 3 все заработало :)
  • 0
    Тогда, как и ожидалось, ваш код был в порядке; только не тогда, когда он запускался постепенно. Не забудьте добавить создание таблицы в onCreate() как указывал jkschneider.
1

Вы можете использовать метод SQLiteOpenHelper onUpgrade. В методе onUpgrade вы получаете oldVersion как один из параметров.

В onUpgrade используйте switch и в каждом из case используйте номер версии, чтобы отслеживать текущую версию базы данных.

Лучше всего переходить от oldVersion в newVersion, увеличивая version на 1 за раз, а затем постепенно обновляя базу данных. Это очень полезно, когда кто-то с базой данных версии 1 обновляет приложение через долгое время, до версии с использованием базы данных версии 7, и приложение начинает сбой из-за некоторых несовместимых изменений.

Затем обновления в базе данных будут выполняться поэтапно, охватывая все возможные случаи, т.е. включающие изменения в базу данных, выполненные для каждой новой версии, и тем самым предотвращение сбоя приложения.

Например:

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

}
  • 0
    Если вы удалите эти операторы break, вам не понадобится цикл
0
Ответ на

@jkschneider правильный. Однако есть лучший подход.

Запишите необходимые изменения в файле sql для каждого обновления, как описано в ссылке 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) ;

Эти .sql файлы будут выполняться в методе onUpgrade() в соответствии с версией базы данных.

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

Пример проекта также представлен в той же ссылке: https://github.com/riggaroo/AndroidDatabaseUpgrades

  • 1
    Я как раз собирался приехать сюда и написать тот же совет. Я рад, что ты уже сделал это. Люди должны обязательно прочитать статью, на которую вы ссылаетесь. Это также то, что Android SQLiteAssetHelper рекомендует для обновлений. Это также то, что CL. (эксперт по SQLite здесь по переполнению стека) рекомендует .
  • 0
    Этот комментарий, что я искал. Скрипты sql, +1

Ещё вопросы

Сообщество Overcoder
Наверх
Меню