Подтвердить что ты не робот

Обновление схемы GreenDao

Я видел еще один вопрос об обновлении/миграции схемы с помощью green dao (здесь)

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

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

Какой самый простой способ добавить новые таблицы в вашу схему без удаления данных, которые ваши пользователи уже сохранили? Конкретный пример будет с большой благодарностью.

Было бы здорово, если greenDao предоставил класс, похожий на DevOpenHelper, который просто добавит новые таблицы/столбцы, которые ранее не существовали в схеме, не отбрасывая сначала существующие вкладки/данные.

4b9b3361

Ответ 1

Наконец-то я успел вникать в это сам и понял, что довольно легко добавить новую таблицу, сохранив данные в старых таблицах.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ. Хотя я понимаю, что эта реализация специфична для моего сценария, я думаю, что это полезно для кого-то вроде меня, который использовал инструмент Android ORM (greenDao) исключительно для работы с SQLite на Android. Я понимаю, что это довольно часто для тех из вас, кто написал ваши собственные запросы на создание таблицы с самого начала, но для тех, кто был защищен от мужества использования SQLite DB с Android, я думаю, что этот пример будет полезен.

ОТВЕТ: Вы можете либо изменить внутренний класс DevOpenHelper, либо создать свой собственный класс. Я решил изменить DevOpenHelper на время, чтобы мой пример был простым - однако обратите внимание, что если вы восстановите свои классы greendao, DevOpenHelper будет перезаписан. Было бы лучше создать собственный класс, например "MyOpenHelper", и использовать его вместо этого.

До моих изменений DevOpenHelper.onUpgrade выглядел так:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 
{
        Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
        dropAllTables(db, true);
        onCreate(db);
}

Вместо того, чтобы отбрасывать все таблицы, взгляните на метод createAllTables, который автоматически генерируется GreenDao.

Перепишите onUpgrade, чтобы проверить, является ли "oldVersion" тот, который вы хотите обновить, а затем вызовите методы createTable для "новых" таблиц. Вот как выглядит мой метод onUpgrade:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 
{
        Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + 

        //Going from older schema to new schema
        if(oldVersion == 3 && newVersion == 4)
        {
            boolean ifNotExists = false;

            //Leave old tables alone and only create ones that didn't exist
            //in the previous schema
            NewTable1Dao.createTable(db, ifNotExists);
            NewTable2Dao.createTable(db, ifNotExists);
            NewTable3Dao.createTable(db, ifNotExists);
            NewTable4Dao.createTable(db, ifNotExists);
        }
        else
        {
            dropAllTables(db, true);
            onCreate(db);
        }
}

Добавление нового столбца будет схожим, за исключением того, что вам нужно будет написать некоторый SQL или взглянуть на автоматически создаваемые инструкции SQL create из greenDao и использовать их.

Чтобы добавить один новый столбец (NEW_COLUMN, предположив его тип INTEGER) в существующую таблицу (EXISTING_TABLE), выполните следующие действия:

db.execSQL("ALTER TABLE 'EXISTING_TABLE' ADD 'NEW_COLUMN' INTEGER");

Для меня прямо сейчас все, что мне нужно было сделать, это добавить новые таблицы, чтобы это оказалось довольно прямолинейным. Надеюсь, кто-то найдет это полезным.

Ответ 2

Я сделал несколько иной подход для автоматической обработки обновлений независимо от того, откуда приходит предыдущий пользователь. Сначала я создал класс, который реализует метод onUpgrade в SQLDatabase

public abstract class AbstractMigratorHelper {

public abstract void onUpgrade(SQLiteDatabase db);
}

Из этого класса наследуется все помощники migrators, которые я объявлю позже

Я напишу пример одного из них

public class DBMigrationHelper5 extends AbstractMigratorHelper {

/* Upgrade from DB schema x to schema x+1 */


public void onUpgrade(SQLiteDatabase db) {
    //Example sql statement
    db.execSQL("ALTER TABLE user ADD COLUMN USERNAME TEXT");
 }
}

После этого вам нужно реализовать логику класса, на самом деле вызываемого при обновлении, где вам нужно будет удалить предыдущий DevOpenHelper для пользовательского, который может выглядеть как

public static class UpgradeHelper extends OpenHelper {

    public UpgradeHelper(Context context, String name, CursorFactory factory) {
        super(context, name, factory);
    }

    /**
     * Here is where the calls to upgrade are executed
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        /* i represent the version where the user is now and the class named with this number implies that is upgrading from i to i++ schema */
        for (int i = oldVersion; i < newVersion; i++) {
            try {
                /* New instance of the class that migrates from i version to i++ version named DBMigratorHelper{version that the db has on this moment} */
                AbstractMigratorHelper migratorHelper = (AbstractMigratorHelper) Class.forName("com.nameofyourpackage.persistence.MigrationHelpers.DBMigrationHelper" + i).newInstance();

                if (migratorHelper != null) {

                    /* Upgrade de db */
                    migratorHelper.onUpgrade(db);
                }

            } catch (ClassNotFoundException | ClassCastException | IllegalAccessException | InstantiationException e) {

                Log.e(TAG, "Could not migrate from schema from schema: " + i + " to " + i++);
                /* If something fail prevent the DB to be updated to future version if the previous version has not been upgraded successfully */
                break;
            }


        }
    }
}

Итак, если вы тщательно назовете своих помощников по миграции (т.е. MigrationHelper5 выполняет переход из схемы 5 в схему 6), вы можете реализовать эту логику, а затем в каждом классе MigratorHelper просто реализуйте вызов execSQL со всем кодом sql, который вам нужен реализовать.

Наконец, еще одно замечание, если вы работаете с proguard, метод find name by class может не работать, поскольку имена классов изменяются при запутывании кода. Вы можете захотеть добавить исключение в файл конфигурации proguard (proguard-rules.pro), чтобы исключить любой класс, который простирается от AbstractMigratorHelper

# Avoid errors when upgrading database migrators

-keep public class * extends yourpackage.locationofyourclass.AbstractMigratorHelper

Ответ 3

Я делаю это несколько иначе.

Я добавляю новые классы @DatabaseTable и любые @DatabaseFields в существующие классы @DatabaseTable и запускаю DatabaseConfigUtil.

Затем я добавлю новый метод в свой класс DatabaseUpgrader и изменю свой DatabaseHelper, изменив значение DATABASE_VERSION и метод onUpdate

public class DatabaseHelper extends OrmLiteSqliteOpenHelper {

    private static final int DATABASE_VERSION = 3;

    @Override
    public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {

        if (newVersion > oldVersion) {
            switch (oldVersion) {
                case 1:
                    DatabaseUpdater.from1to2(connectionSource);
                    DatabaseUpdater.from2to3(connectionSource);
                    break;

                case 2:
                    DatabaseUpdater.from2to3(connectionSource);
                    break;

                default:
                    onCreate(db);
            }
        }
    }

    public static DatabaseHelper getInstance() {

        return DatabaseHelper.mHelper;
    }

    public static void setInstance(Context context) {

        DatabaseHelper.mHelper = new DatabaseHelper(context);
    }

    …
}

И затем в классе DatabaseUpdater

public class DatabaseUpdater {

    private static final String TAG = "DatabaseHelper";

    public static void from1to2(ConnectionSource connectionSource) {

        try {
            DatabaseHelper helper = DatabaseHelper.getInstance();

            //Example add a table
            TableUtils.createTable(connectionSource, AnotherEntity.class);


        } catch (SQLException e) {
            Log.e(TAG, "Error upgrading database to v2: ", e);
        } catch (java.sql.SQLException e) {
            e.printStackTrace();
        }

    }

    public static void from2to3(ConnectionSource connectionSource) {

        try {
            DatabaseHelper helper = DatabaseHelper.getInstance();

            //Example add a field to a table
            RuntimeExceptionDao<MyEntity, Integer> myDao = helper.getMyDao();
            diaryDao.executeRaw("ALTER TABLE myEntity ADD firstNewField");
            diaryDao.executeRaw("ALTER TABLE myEntity ADD anotherNewField");


        } catch (SQLException e) {
            Log.e(TAG, "Error upgrading database to v3: ", e);
        }

    }
}