26

我看到了另一个关于使用 green dao 进行架构升级/迁移的问题(这里

该答案中有很多链接可以在进行架构升级时使用一个好的模式 - 但是没有示例说明您对数据实际执行了什么操作以正确迁移它,而且我找不到任何东西。

就我而言,我的迁移非常简单——我不希望转换任何现有数据,我只需要在我的模式中添加一些新表,我怀疑这是一种相当常见的情况。

在不删除用户已经保存的数据的情况下,将新表添加到架构中的最简单方法是什么?一个具体的例子将不胜感激。

如果 greenDao 提供了一个类似于 DevOpenHelper 的类,该类将简单地添加以前不存在于模式中的新表/列,而不首先删除现有的表/数据,那就太棒了。

4

4 回答 4

44

我终于有时间自己深入研究这一点,并意识到在旧表中保留数据的同时添加新表非常容易。

免责声明:虽然我意识到这个实现是针对我的场景的,但我认为它对像我这样专门使用 Android ORM 工具 (greenDao) 来处理 Android 上的 SQLite 的人很有帮助。我知道这对于那些从一开始就编写自己的表创建查询的人来说是很常见的,但是对于那些没有在 Android 上使用 SQLite DB 的胆量的人来说,我认为这个示例会有所帮助。

解答: 您可以修改 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);
}

不要删除所有表,而是看一下 GreenDao 自动生成的 createAllTables 方法。

重写 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 或查看 greenDao 自动生成的 SQL 创建语句并利用这些语句。

要将单个新列(NEW_COLUMN,假设它是 INTEGER 类型)添加到现有表 (EXISTING_TABLE),请执行以下操作:

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

现在对我来说,我需要做的就是添加新表格,所以这最终变得相当简单。希望其他人觉得这很有用。

于 2013-05-05T15:43:08.663 回答
9

无论以前的用户来自哪里,我都采用了一种稍微不同的方法来自动处理更新。首先,我创建了一个在 SQLDatabase 上实现 onUpgrade 方法的类

public abstract class AbstractMigratorHelper {

public abstract void onUpgrade(SQLiteDatabase db);
}

从这个类将继承我将在之后声明的所有迁移器助手

我将写其中一个的例子

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 类中使用您需要实现的所有 sql 代码实现 execSQL 调用。

最后再说一点,如果您使用 proguard,则按类查找名称的方法可能不起作用,因为在混淆代码时会更改类名。您可能需要考虑在 proguard 配置文件 (proguard-rules.pro) 上添加一个例外,以排除从 AbstractMigratorHelper 扩展的任何类

# Avoid errors when upgrading database migrators

-keep public class * extends yourpackage.locationofyourclass.AbstractMigratorHelper
于 2014-09-23T09:16:44.537 回答
5

我做的方式略有不同。

我将新的@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);
        }

    }
}
于 2013-08-16T06:56:18.650 回答
0

回答@MBH 在第一个答案时发布的问题。我也没有在这篇文章中找到答案,因此添加了。

GreenDAO 使用 build.gradle 文件中的模式版本号。Gradle 文件应包含以下内容

android {
   ...
}
greendao {
   schemaVersion 1
}

有关更多信息,请参阅此链接。然后在升级时将此数字更改为 2 或任何增量。基于这个数字,GreenDAO 从 android.database.sqlite.SQLiteDatabase.DatabaseOpenHelper.java 调用下面的 API

public DatabaseOpenHelper(Context context, String name, int version)

作为 Sqlite DB 升级的标准方法,它调用以下 API

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)

正如其他答案所暗示的那样,此方法可以在派生类中被覆盖,并且可以处理任何项目特定的升级。希望这可以帮助。

于 2019-02-28T17:13:51.210 回答