greenDao Schema Upgrade

I do it a slightly different way.

I add my new @DatabaseTable classes and any @DatabaseFields to existing @DatabaseTable classes and run DatabaseConfigUtil.

Then I'll add a new method to my DatabaseUpgrader class and modify my DatabaseHelper, changing the DATABASE_VERSION value and the onUpdate method

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

    …
}

And then in the DatabaseUpdater class

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

    }
}

I finally had time to dig in to this myself and realized it's quite easy to add a new table while retaining data in old tables.

DISCLAIMER: While I realize this implementation is specific to my scenario, I think it's helpful for someone like me who has used an Android ORM tool (greenDao) exclusively to deal with SQLite on Android. I understand this is pretty common for those of you who have written your own table creation queries from the beginning, but for someone who has been sheltered from the guts of using a SQLite DB with Android, I think this example will be helpful.

ANSWER: You can either modify the DevOpenHelper inner class or create your own class. I chose to edit DevOpenHelper for the time being to keep my example simple - however, note that if you regenerate your greendao classes, DevOpenHelper will be overwritten. It would be a better idea to create your own class like "MyOpenHelper" and use that instead.

Before my changes, DevOpenHelper.onUpgrade looked like this:

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

Instead of dropping all tables, take a look at the createAllTables method that is auto-generated by GreenDao.

Rewrite onUpgrade to check if the "oldVersion" is the one you want to upgrade from, then only call the createTable methods for "new" tables. Here is what my onUpgrade method looks like now:

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

Adding a new column would be similar, except you'd have to write some SQL or take a look at the auto-generated SQL create statements from greenDao and leverage those.

To add a single new column (NEW_COLUMN, assuming it's an INTEGER type) to an existing table (EXISTING_TABLE), do the following:

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

For me right now, all I needed to do was add new Tables so this ended up being rather straight forward. Hopefully someone else finds this useful.


I made an slightly different approach to handle the updates automatically no matter where the previous user comes from. First I created a Class that implements the method onUpgrade on a SQLDatabase

public abstract class AbstractMigratorHelper {

public abstract void onUpgrade(SQLiteDatabase db);
}

From this class will inherit all the migrators helpers I will declare afterwards

I will write an example of one of them

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

After this you need to implement the logic on the class that is actually called on upgrade, where you will need to remove the previous DevOpenHelper for a custom one that could look like this

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


        }
    }
}

So if you are careful naming your Migration Helpers (i.e. MigrationHelper5 does the migration from schema 5 to schema 6) you can implement this logic and then in every MigratorHelper class just implement the execSQL call with all the sql code that you need to implement.

Finally one more remark, if you are working with proguard, the method find name by class might not work, since class names are changed when obfuscating the code. You might want to consider add an exception on the proguard configuration file (proguard-rules.pro) to exclude any class that extend from AbstractMigratorHelper

# Avoid errors when upgrading database migrators

-keep public class * extends yourpackage.locationofyourclass.AbstractMigratorHelper