I have an app that is being used by beta testers. My original schema did not end up working for my actual implementation of the feature I'm currently working on. I want to update the database. I do not want my testers to lose their data.
My understanding is that the steps for this are:
1) Leave my SQLiteOpenHelper class's onCreate method AS IS. I leave it as it is currently / originally written, the "version one" schema as I think of it.
UPDATE 2015-04-19: Step One is WRONG. See my own (accepted) answer below for details.
2) Fill in my SQLiteOpenHelper class's onUpgrade method. Here I make all the changes to go from the first schema to the second. (and in the future maybe write the changes needed to go from the second schema to a third)
3) Update the database version. In my app, I keep this as a static var. When I'm ready to pull the trigger and update the database, I change this var (DB_VERSION) from 1 to 2. This var being 2 versus the app installed on the device having a version of 1 is what triggers onUpgrade() to be called the first time the database is opened (in my case, the app's onCreate).
public static final int DB_VERSION = 1;
...
class DBHelper extends SQLiteOpenHelper {
public DBHelper() {
super(context, DB_NAME, null, DB_VERSION);
}
4) Run my app and cross my fingers. If I get an error / made a typo in my SQL, I uninstall and run again.
Can anyone verify if that is correct? Or if I'm overlooking something?
The tutorials I've found -- when they don't just drop and recreate the database, changing the onCreate to the new schema -- don't really discuss when you already have an app and need to make a change. I guess it's not relevant to a beginner, and assumed knowledge for a non-beginner? For upgrades with user data that can't be thrown away, I only have been able to find specific question/answers about a single aspect of the upgrade, not a comprehensive guide. I'm afraid I could be missing something that's so basic/important that it "goes without saying", hence my apprehension/fear to just do it. I've searched as best as I am able, could really use some guidance! Thanks in advance for any help!
That is correct. You are increasing version and updating db.
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(oldVersion < 2) {
db.execSQL("blablabla");
}
if(oldVersion < 3) {
db.execSQL("new blablabla");
}
if(oldVersion < 4) {
db.execSQL("newest blablabla");
}
}
> Can anyone verify if that is correct?
Yes
Thats the way it worked for my android apps. My code looks like this:
public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION_3_MYTABLE_WITH_NOTES = 3;
public static final int DATABASE_VERSION_4_CATEGORY_ACTIVE = 4;
public static final int DATABASE_VERSION_5_REPORT_VIEW = 5;
// points to latest version
public static final int DATABASE_VERSION = DatabaseHelper.DATABASE_VERSION_5_REPORT_VIEW;
public DatabaseHelper(final Context context, final String databaseName) {
super(context, databaseName, null, DatabaseHelper.DATABASE_VERSION);
....
}
#Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
final int newVersion) {
Log.w(this.getClass().toString(), "Upgrading database from version "
+ oldVersion + " to " + newVersion + ". (Old data is kept.)");
if (oldVersion < DatabaseHelper.DATABASE_VERSION_3_MYTABLE_WITH_NOTES) {
this.version3Upgrade_MYTABLE_WITH_NOTES(db);
}
if (oldVersion < DatabaseHelper.DATABASE_VERSION_4_CATEGORY_ACTIVE) {
this.version4Upgrade_CATEGORY_ACTIVE(db);
}
if (oldVersion < DatabaseHelper.DATABASE_VERSION_5_REPORT_VIEW) {
this.version5Upgrade_REPORT_VIEW(db);
}
}
private void version3Upgrade_MYTABLE_WITH_NOTES(final SQLiteDatabase db) {
// added MYTABLE.notes
db.execSQL("ALTER TABLE " + MyTable.TABLE
+ " ADD COLUMN " + MyTable.COL_NOTES + " TEXT");
}
I have one const and one method to upgrade from one version to the next.
Sorry self-from-3-months-ago, turns out that's WRONG!
The problem is in step one. The onCreate is NOT left as is. It needs to create the newest version of the database. onUpdate is only called when an EXISTING user updates to the newest app release. Anyone doing a CLEAN INSTALL would end up with the old scheme, never calling onUpdate.
Here is the skeleton of my new code, which seems to handle both clean installs and upgrades. (At least I hope it does.)
class DBHelper extends SQLiteOpenHelper {
public DBHelper() {
super(context, DB_NAME, null, DB_VERSION);
Log.i(TAG, "DBHelper called with DB_NAME " + DB_NAME + " and DB_VERSION " + Integer.toString(DB_VERSION));
}
#Override
public void onCreate(SQLiteDatabase db) {
setupV1(db);
upgradeV2(db);
}
#Override
public void onUpgrade(
final SQLiteDatabase db, final int oldVersion,
final int newVersion)
{
int upgradeTo = oldVersion + 1;
Log.i(TAG, "Upgrading database to version " + Integer.toString(upgradeTo));
while (upgradeTo <= newVersion)
{
switch (upgradeTo)
{
// Database v2. Added X Functionality
case 2:
upgradeV2(db);
break;
// Database v3 (not created yet)
// case 3:
// // something here
// break;
}
upgradeTo++;
}
}
private void setupV1(final SQLiteDatabase db) {
// CODE HERE TO CREATE VERSION / SCHEMA ONE OF MY DATABASE
}
private void upgradeV2(final SQLiteDatabase db) {
// CODE HERE TO UPDATE FROM VERSION ONE TO VERSION TWO
}
}
Related
I want to add new columns to a SQLite database, but I already released my app on the Play Store so, if I edit it, users need to uninstall and reinstall the app,
but I don't want that. Please, help, I am new to Android.
(1) Increment (or simply change) your database version
(2) It would lead to onUpgrade() method call
(3) Execute your query(for adding a new column) in the onUpgrade() method.
The Right Way of doing is mentioned here in this blog.
Sometimes user may update the version 1.5 from the version 1.1.In other words, They may skip the other versions between the 1.1 to 1.5. You might changed database couple of time between 1.1 to 1.5. So in order to give the user a benefit of all the database changes, you need to take of onUpgrade() method as below.
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
db.execSQL(DATABASE_ALTER_TEAM_1);
}
if (oldVersion < 3) {
db.execSQL(DATABASE_ALTER_TEAM_2);
}
}
Hope it helps.
You need to changes in following method of Database Class
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "ALTER TABLE " + TABLE_SECRET + " ADD COLUMN " +
"name_of_column_to_be_added" + " INTEGER";
db.execSQL(sql);
}
im using SQLite OpenHelper to handle database version and it worked fine, my problem is, if i set some updates and new users install my app, the will not get the UPGRADES, because it will run the onCreate using the last DB_VERSION
explaining:
private static final int DB_VERSION = 3;
Context context;
public DB(Context c) {
super(c, DB_NAME, null, DB_VERSION);
context = c;
}
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE table1...");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d("DBUpgrade", "Atualizando DATABASE");
switch (oldVersion) {
case 2: {
execSQLFromFile(db, "sql2");
}
case 3: {
execSQLFromFile(db, "sql3");
}
}
scenarios:
User 1 has installed since first app release
-> he will get the all the upgrades
User 2 installed for the first time in his device the last release of my apk that has the db_version 3
-> he will not get the upgrades, just the onCreate
ofc i know why this happens but i dont know how to solve it by a simple way, the only i way i throught was to call the onUpgrade(db, 0, DB_VERSION); this enter in the onUpgrade but dont call the cases inside the switch i dont know why, since i send 0, it should call case: 2, because i dont have the break isnt it?
BUT doing this i loose the OnUpgrade for the next version, it will always check the oldVersion, and redo the same code so it will crash because Unique column, alter columns anything
so i still ended without way to onfirst install do the upgrades too =(
User 2 installed for the first time in his device the last release of my apk that has the db_version 3 -> he will not get the upgrades, just the onCreate
There are no "upgrades", as there is no database to upgrade. onCreate() must be implemented to create a database with the proper (latest) schema.
I am developing a database application and I am stuck up at one place.
The scenario is: I have a database and I am maintaining the version of the database.
It is the same as that of the application version.
Now I am implementing the database in version 1. There are 3 tables getting created in version 1. In version 2, I am upgrading the database by adding one more table. Hence, the query is in the upgrade().
Now, what if the user installs version 2. The onUpgrade() will not get called because there is no database. Hence, the onCreate() will be called and the consequence will be it will create only 3 tables.
I was thinking to call the onUpgrade() explicitly in the onCreate(). But many developers on stackoverflow have suggested not to call it explicitly.
I am completely stranded.
Thanks in advance.
I know it's an old post, you have certainly found a solution, but I find the question valid (upvoted) and as it did not receive an answer, here are my 2 cents:
Calling onUpgrade() from onCreate() should work (I did not test it), but there is a better way to organize you helper (see snippet hereafter). The key point is that both those methods are just entry points.
public class SampleDBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "mydatabase";
private static final int DB_VERSION = 2;
public SampleDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
updateDatabase(db, 0, DB_VERSION);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
updateDatabase(db, oldVersion, newVersion);
}
private void updateDatabase(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 1) {
// Create 3 initial tables
}
if (oldVersion < 2) {
// Create the new table
}
}
}
The advantages of this kind of helper:
You never need to change onCreate() and onUpgrade() methods, only updateDatabase().
For each new version, you just need to :
increment DB_VERSION
add a new section if (oldVersion < N) { // do the upgrade }.
Credit : Strongly inspired from Head First Android Development, Dawn and David Griffiths, O'Reilly, 2015
I have seen another question about schema upgrade/migration using green dao (here)
There are lots of links in that answer for a good pattern to use when doing schema upgrades - however there are no examples of what you actually do to your data to migrate it properly and I'm having trouble finding anything.
In my case, my migration is incredibly straight forward - I do not wish to transform any existing data, I simply need to add some new tables to my schema, which I suspect is a fairly common situation.
What is the easiest way to add new tables to your schema without deleting data your users have already saved? A specific example would be greatly appreciated.
It would be awesome if greenDao provided a class similar to DevOpenHelper that would simply add new tables/columns that didn't previously exist in the schema without dropping existing tabes/data first.
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
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);
}
}
}
To answer question posted by #MBH on first answer. Also I did not find the answer in this post hence adding.
GreenDAO uses the schema version number from build.gradle file. Gradle file should contain below
android {
...
}
greendao {
schemaVersion 1
}
Refer this link for more information. Then on upgrade change this number to 2 or any increment. Based on that number GreenDAO calls below API from android.database.sqlite.SQLiteDatabase.DatabaseOpenHelper.java
public DatabaseOpenHelper(Context context, String name, int version)
As standard approach by Sqlite DB upgrade, it calls below API
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
As other answers suggest, this method can be overridden in derived class and can handle any project specific upgrades.
Hope this helps.
Typically, for a WinForm or a Web App, I create the database and tables through the RDBMS or through a separate install process. However, I haven't seen anything of the sort in Android. All the examples I've seen have the database creation scripts embedded in an activity like this.
The best thing I can come up with now is to call a method from the data access constructor to check whether the database is installed - if not - install it. However, this seems like a lot of overhead to me.
What's the cleanest way to execute a android database install and then forget about it?
When using SQLLiteOpenHelper (http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html), the onCreate method will be called only if the database doesn't exist. onUpgrade will be called when a new version of the database is introduced.
IF the database already exists, and no version upgrade occured, these methods won't be executed.
There is no need for implementing if-else checks in your activity.
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion)
{
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
db.execSQL(DATABASE_UPGRADE);
}
}