I have an existing database based on SQLiteOpenHelper that has several versions and code to upgrade it and that works fine. But in case a user installs an older version of the app (that expects a lower database version) it will currently crash - the ContentProvider using it can't access the database. I'd like to prevent it from crashing but I don't want to actually downgrade the database - adding the code to do that would be pain. Dropping all tables would certainly work but starting with a fresh file is imo cleaner and less error prone.
That's about what the database helper looks like - nothing special
public class MyDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 3;
private static final String DATABASE_NAME = "my.db";
public MyDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
onUpgrade(db, 0, DATABASE_VERSION);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion < 1) db.execSQL("CREATE TABLE A...");
if (newVersion < 2) db.execSQL("CREATE TABLE B...");
if (newVersion < 3) db.execSQL("CREATE TABLE C...");
}
#Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// I'd like to delete the database here
// the only problem is that I can't from here
// since this is called in the middle of getWritableDatabase()
// and SQLiteDatabase has no .recreate() method.
}
}
The possible ways I've come up to do that are:
Do it from the outside: catch exceptions in the ContentProvider, delete the file and request to open the database again. - I don't like that since it's not the responsibility of the provider.
Replacing SQLiteOpenHelper with my own copy of that class that deletes the file instead of calling onDowngrade - Problem is that it's using package private parts of SQLiteDatabase (e.g. .lock()) which I can't replace without duplicating SQLiteDatabase too (that would probably result in duplicating the whole sqlite stack).
Is there any good approach to do that or do I have to go the DROP TABLES way e.g. like described here?
I've figured out a way that works nicely by extending SQLiteOpenHelper and all I need to do in MyDbHelper is to extend this class.
public abstract class DeletingSQLiteOpenHelper extends SQLiteOpenHelper {
private static final String TAG = DeletingSQLiteOpenHelper.class.getSimpleName();
private final File mDatabaseFile;
public DeletingSQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
DatabaseErrorHandler errorHandler) {
super(context, name, factory, version, errorHandler);
mDatabaseFile = context.getDatabasePath(name);
}
public DeletingSQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
mDatabaseFile = context.getDatabasePath(name);
}
#Override
public synchronized SQLiteDatabase getWritableDatabase() {
try {
return super.getWritableDatabase();
} catch (SQLiteDowngradeFailedException e) {
// that's our notification
}
// try to delete the file
mDatabaseFile.delete()
// now return a freshly created database
return super.getWritableDatabase();
}
#Override
public final void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// throwing a custom Exception to catch it in getWritableDatabase
throw new SQLiteDowngradeFailedException();
}
// that's the exception
static class SQLiteDowngradeFailedException extends SQLiteException {
public SQLiteDowngradeFailedException() {}
public SQLiteDowngradeFailedException(String error) {
super(error);
}
}
}
Related
I'm working on upgrading sqlite databases in android. I think I got the principles, but I can't understand the behavior of - in my understanding - similar code versions.
When I directly excecute the "Alter Table"-Command inside the overridden onUpgrade-procedure (Code 1 below), everything is fine. If I call a procedure using exactly the same (sql and java) code inside it (Code 2), the database is not affected. However, there is no error notice, and if I run the sql-code in a sqlite manager after pulling the DB-File using adb it works fine as well.
It would be nice if anyone could explain what is the problem with the "seperated version"?
Note: For creating the database, there is no problem in calling a routine from onCreate...
"Integrated" Version, Code 1, Working:
public class DBHandler extends SQLiteOpenHelper {
private static final String DBName = "DynamicValueTracker.db";
private static final int DBVersion = 2;
public DBHandler(Context context) {
super(context, DBName, null, DBVersion);
mContext = context;
}
#Override
public void onUpgrade(SQLiteDatabase db, int OldVersion, int NewVersion) {
switch (NewVersion) {
//(deleted further cases for posting question...)
//This works
case 2:
String sqlcmd = "ALTER TABLE countries ADD COLUMN capital TEXT DEFAULT null;";
db.execSQL(sqlcmd);
break;
}
}
}
"Seperated" Version, Code 2, Not Working:
public class DBHandler extends SQLiteOpenHelper {
private static final String DBName = "DynamicValueTracker.db";
private static final int DBVersion = 2;
public DBHandler(Context context) {
super(context, DBName, null, DBVersion);
mContext = context;
}
#Override
public void onUpgrade(SQLiteDatabase db, int OldVersion, int NewVersion) {
switch (NewVersion) {
//(deleted further cases for posting question...)
case 2:
DBUpgrade_V1_to_V2(db);
break;
}
}
}
public void DBUpgrade_V1_to_V2(SQLiteDatabase db) {
//This is excecuted without throwing errors but does not change the database table
String sqlcmd = "ALTER TABLE countries ADD COLUMN capital TEXT DEFAULT null;";
db.execSQL(sqlcmd);
}
}
I wrote an application in android that using DB.
In the next version I want to add new column to the DB but without re-installing the application. How can I upgrade the table on missing column exception?
You can simply do it by changing(increment) database version
public class Databas extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 2;
Database(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(oldVersion <= DATABASE_VERSION)
db.execSQL("//write table altering query here ");
onCreate(db);
}
increment database version value with 1 from your current database version value
This can be easily done by using SQLiteOpenHelper. Let say your current database version is 1. You just change it to 2. Then if there is any version 1 database will be automatically update to version 2 and onUpgrade method will be invoked.
private static final String DATABASE_NAME = "data";
private static final String DATABASE_TABLE = "notes";
private static final int DATABASE_VERSION = 2;
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) {
if(oldVersion <= 1)
db.execSQL("Do you table alter here ");
onCreate(db);
}
}
In my app I have used Sqlite. When I test the app in the emulator it runs ok but when I test the app on my device, it fails because the Helper class not pass by the OnCreate method. My java code is below.
public static void startDB(Context context) {
DBHelper = new DataBaseHelper(context);
DBHelper.getWritableDatabase();
}
private static class DataBaseHelper extends SQLiteOpenHelper {
public DataBaseHelper(Context context) {
super(context, NOMBRE_BASE_DATOS, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(USER_CREATE);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
for (String table : ALL_TABLES) {
db.execSQL("DROP TABLE IF EXISTS " + table);
}
onCreate(db);
}
}
The framework only calls onCreate() if the database does not exist.
To force onCreate(), remove the old database file: clear your app's data in app manager, or just uninstall and reinstall.
This is the fist time I've used SQLiteOpenHelper (or databases on android). When I get a writeable database I was wondering why onCreate isnt being called on each new instance of the class. Am I doing something wrong?
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "MyDatabase.db";
private static final int DATABASE_VERSION = 1;
private String PrSQLcmd = "";
public DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL("CREATE TABLE IF NOT EXISTS Contact(Firstname TEXT, LastName TEXT");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
In SQLiteOpenHelper, the meaning of 'onCreate' is different from what it is in an Activity. Here,'onCreate' is called only once, which is the first time you create the database. The next time you run the app, the database is already there, so it won't call 'onCreate'. Your object level initialization should be done in the constructor and not in 'onCreate'
To see 'onCreate' being called, either manually delete the db file, or simply uninstall the app.
I would to erase and recreate the database for my app each time I send a new version of my app from Eclipse to my phone (I am developing and changing my database very often). What is the easiest way to do this?
public class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, dbName, null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE USERS (username TEXT, password TEXT);");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS USERS");
onCreate(db);
}
i use this one in my code :) change super(context, dbName, null, <database version number>); and it will execute onUpgrade method.
If this is something you do not wish to do once it is live, you can just clear data from your application manager each time. Also, you can make the database version number different and have the onUpgrade destroy and recreate the DB
private static final String DATABASE_NAME = "MyDbName";
private static final int DATABASE_VERSION = 6;
private static class DbOpenHelper extends SQLiteOpenHelper{
public DbOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
onUpgrade(this.getWritableDatabase(), 0, 0);
//
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TableHelper1.CREATE_TABLE_QUERY);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+ TableHelper1.TABLE_NAME);
onCreate(db);
}
}
In Eclipse, go into Debug Configurations, then Android Application on the left hand side. Select the debug configuration that you are using and then select the Target tab.
At the bottom, you'll see a checkbox labeled "Wipe User Data".
If you check that, everytime you launch the simulator, the data associated with your application (including the SQLite database) will be erased.
Your application will then call onCreate() for your SQLiteOpenHelper, and your database will be recreated.