SQLiteOpenHelper onUpgrade called with wrong oldVersion? - android

I am using GreenDao in my Android app, and have a subclass of SQLiteOpenHelper which I call DBHelper:
public class DBHelper extends DaoMaster.OpenHelper {
// DaoMaster.OpenHelper extends SQLiteOpenHelper and passes in `SCHEMA_VERSION` which for me is 32
public static final String DATABASE_NAME = "my_app.db";
public DBHelper(Context context) {
super(context, DATABASE_NAME, null);
}
#Override
public void onCreate(SQLiteDatabase arg0) {
...
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
...
}
}
I have occasionally been seeing exceptions coming from within that onUpgrade method on code paths that only happen if oldVersion is less than 11 - my current schema version is 32, and version 11 is now well over six years old.
This is causing some very serious problems, because as part of the migration path from versions less than 11 I migrated to some new database tables, meaning the rollback involves dropping database tables, and as a result I've had some reports of data loss.
I don't really believe that oldVersion is correct - the bug reports I've had from users all say they installed the app for the first time quite recently. Does anybody have any ideas why this might happen?
Since anybody who has used the app in the last six years ought to have done the migration by now, I'm thinking I should just remove any code related to migrations from oldVersions less than 11 - am I likely to regret that, will that introduce any new problems?

Related

App doesn't show database changes with SQLite Asset Helper

I'm writing an app that comes packaged with an SQLite database.
I'm in the process of gradually adding to the database, but when I compile the code in Android Studio the app doesn't see the latest update to it.
The workaround I've found is changing the filename of the database and updating it in the code, but that's going to get very tiresome if I'm making frequent updates, and I feel there must be a better way.
For what it's worth, here's the relevant code snippet:
public class DatabaseHelper extends SQLiteAssetHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "database5.db";
private static final String BOOKS = "books";
private static final String AUTHORS = "authors";
public DatabaseHelper (Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// etc
}
You need to increment the version number then use the setForcedUpgrade(). Something like this:
private static final int DATABASE_VERSION = 2;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// call this method to force a database overwrite every time the version number increments:
//setForcedUpgrade();
}
You can found the details in the sample project.
when I compile the code in Android Studio the app doesn't see the latest update to it
SQLiteAssetHelper only copies the database out of assets if the database does not already exist.
I feel there must be a better way
If you are not modifying the database at runtime, and if you plan on distributing updates to that database in the form of fresh assets, you can use setForcedUpgrade(), as is covered in the documentation.
Otherwise, you can uninstall the app when you change the database, or clear the app's data in Settings when you change the database.
Just change your sqlite database file name from asset folder
[here i have change name Quran.db to Quran1.db]
And from class where you have access it.
[from class also i have changed Quran.db to Quran1.db]
This method worked fine for me.

Android ORMLite DbHelper onCreate() not called even after uninstall

I'm getting this error when I run my app for the first time after re-install:
android.database.sqlite.SQLiteException: no such table
(This error happens when my app tries to read from the database)
For some reason the onCreate() method in DBHelper is not getting called and therefore the tables are not getting created. I followed the advice from other question and tried calling getWritableDatabase(), also tried a create() call to insert data in some table, but still no luck: onCreate is never called.
I got it to work however by changing the DATABASE_VERSION value to 2. But that doesn't make sense since this is a brand new installation after uninstall.
Also I found that before the SQL read error the database got created but it has only 1 table "android_metadata" (not created by me).
I'm posting some code here for reference
public class DatabaseHelper extends OrmLiteSqliteOpenHelper{
private static final String DATABASE_NAME = "RoutePlanner.db";
private static final int DATABASE_VERSION = 1;
private Dao<Trip, Integer> tripDAO = null;
private RuntimeExceptionDao<Trip, Integer> tripRunTimeDAO = null;
...
}
#Override
public SQLiteDatabase getWritableDatabase() {
return super.getWritableDatabase();
}
public DatabaseHelper(Context context){
super(context, DATABASE_NAME,null, DATABASE_VERSION, R.raw.ormlite_config);
}
#Override
public void onCreate(SQLiteDatabase db, ConnectionSource source) {
try {
Log.i(DatabaseHelper.class.getSimpleName(), "onCreate");
TableUtils.createTable(source, Trip.class);
...
} catch (SQLException ex) {
Log.e(DatabaseHelper.class.getSimpleName(), "Error creating db", ex);
throw new RuntimeException(ex);
}
}
OK, I found the problem, hope this explanations helps others on what NOT to do. The issue was that I had a separate calendar module which I wanted to access my Database. To make things 'simpler' I created a separate DatabaseHelper on that module to access the same SQLite databse as my main module. The existance of the 2nd DatabaseHelper was causing all my issues. Solutions are either join the 2 modules into one, or use a Database Service Provider

Android SQLiteAssetHelper Can't upgrade read-only database from version 1 to 2:

I want to create an offline dictionary app, that need sometimes to get update and old database will be replaced by new one. It's what I want to do, and I did something like this with SQLiteAssetHelper Library:
Note: SQLiteAssetHelper will copy database from assets folder into app data folder
public class MyDb extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "db.sqlite";
private static final int DATABASE_VERSION = 1;
public MyDb(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
now i want to update database, after puting new db.sqlite file into assets folder, I have manipulate my codes like this:
public class MyDb extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "db.sqlite";
private static final int DATABASE_VERSION = 2;
public MyDb(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
but when i compiled and run, it says: Can't upgrade read-only database from version 1 to 2
what is the solution?
by clearing app data, it will works fine...
SORRY FOR MY BAD ENGLISH...
Quoting the documentation:
If you have a read-only database or do not care about user data loss, you can force users onto the latest version of the SQLite database each time the version number is incremented (overwriting the local database with the one in the assets) by calling the setForcedUpgrade() method in your SQLiteAsstHelper subclass constructor.
You can additionally pass an argument that is the version number below which the upgrade will be forced.
Note that this will overwrite an existing local database and all data within it.
You are not calling setForcedUpgrade() from your constructor.
You should call setForcedUpgrade(); after your Constructor :
public class MyDb extends SQLiteAssetHelper {
private static final int DATABASE_VERSION = 2;//+1
public MyDb(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
setForcedUpgrade();
}
}
Please note that deleting the old db and creating new one in onUpgrade is not the right solution. Older data has to be migrated. Incase you are not interested in maintaining older data ov version 1, it is alright.
Right way is to use onUpgrade to modify the schema. Most of the time altering the schema required recreating table. In such cases follow below approach
rename existing table (say table_xyz) to be altered to some temp
name (say temp_table_xyz)
create the table (table_xyz) with new schema
copy the contents from temp table temp_table_xyz to the
new table_xyz
It's solved by this:
#Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion != newVersion) {
context.deleteDatabase(DATABASE_NAME);
new MyDb(context);
}else
super.onUpgrade(db, oldVersion, newVersion);
}
It's very useful for some database that we don't need old data when getting update.

SQLiteOpenHelper synchronization

So I've come up with some idea and I'm wondering if it is realizable.
Let's say I've multiple tables(database models) each of them is represented by some class.I don't wont to use singleton pattern with the open helper so I've created some simple class to provide single instance of the database.My idea is that as long as all tables hold reference to SQLiteDatabase(returned by the open helper) they will all work with the same DB instance and probably won't be needed to synchronized the work with the database since the open helper do this.When the last table finish it's work the GC will collect the open helper (since the last reference will be weak reference) -> finalize() is called and I close the db during this method to prevent any warning from OS. My question is: Is this could work?Will it close automatically the DB and will it leak or throw some exception?
Here is my class:
public class DatabaseHelper {
private static WeakReference<SomeCustomOpenHelper> sDBOpenHelper;
private void notifyDBCreate(SQLiteDatabase db) {
for (DBTable table : mTables) {
table.onDBCreate(db);
}
}
private void notifyDBUpgrade(SQLiteDatabase db) {
for (DBTable table : mTables) {
table.onDBUpgrade(db);
}
}
public SQLiteDatabase getDatabase(boolean readOnly) {
SomeCustomOpenHelper dbHelper = sDBOpenHelper.get();
if (dbHelper == null) {
dbHelper = new SomeCustomOpenHelper(context, name, factory, version, new DatabaseEventsCallback());
sDBOpenHelper = new WeakReference<SomeCustomOpenHelper>(dbHelper);
}
if (readOnly) {
return dbHelper.getReadableDatabase();
} else {
return dbHelper.getWritableDatabase();
}
}
private class DatabaseEventsCallback implements IDatabaseEventsCallback {
#Override
public void onCreate(SQLiteDatabase db) {
notifyDBCreate(db);
}
#Override
public void onUpgrade(SQLiteDatabase db) {
notifyDBUpgrade(db);
}
}
interface IDatabaseEventsCallback {
void onCreate(SQLiteDatabase db);
void onUpgrade(SQLiteDatabase db);
}
private static class SomeCustomOpenHelper extends SQLiteOpenHelper {
private IDatabaseEventsCallback mCB;
public SomeCustomOpenHelper(Context context, String name, CursorFactory factory, int version, IDatabaseEventsCallback cb) {
super(context, name, factory, version);
mCB = cb;
}
#Override
public void onCreate(SQLiteDatabase db) {
mCB.onCreate(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
mCB.onUpgrade(db);
}
#Override
protected void finalize() throws Throwable {
this.close();
super.finalize();
}
}
}
Did not really know the answer neither, but got interested and looked it up.
The answer is written out properly here;
http://blog.foxxtrot.net/2009/01/a-sqliteopenhelper-is-not-a-sqlitetablehelper.html
But basically the core of the info is;
I created three SQLiteOpenHelper classes, one for each table, even though they all referenced only a single database file.
Here is where everything fell apart. Android maintains Versions for databases based on the package it’s associated with, the name of the database, and the version number you provide. The package and name go into decided what the path on the device will be, while the version is stored (somewhere) on the device so that it knows when it needs to call an OpenHelper’s onUpgrade event handler. It turns out that if, in the SQLiteOpenHelper Constructor, it determines that the database already exists, it won’t call your onCreate or onUpgrade methods at all, even if the particular class which is making the call has never been called before.
I've been through the same issue when I was working on a project. I also went crazy on the doubt if the static instance was using enough memory and causing a considerable memory leak.
I'm not sure if creating a weak reference would guarantee that database instance would be collected. However a possible workaround could be : Assigning a null value to static database instance once your all database transaction is done and you've close the database. This might ensure that the database instance no more allocates any memory.
Let me know if this works or if there is a better work-around.
You can do so. As you say the locking should be happening on the SQLite and I've never heard issues around that so you should be fine with this.
The only restriction you have is that all the tables will have to go into the same database since Android for now just allows you to have one file.
Closing the database is a different thing, that's why it is actually interesting to use the singleton pattern (you avoid closing + opening all the time).
Nonetheless with your approach you just need to make sure to close the db whenever you are done with it. As far as I'm concerned this is not automatically done.
Additionally Lars Vogel has written extremely useful and detailed articles around DB access in Android. You might want to have a look there. http://www.vogella.com/articles/AndroidSQLite/article.html
you can use one open helper for all the table .i am using the single instance in my app also like this .
public static synchronized DatabaseHelper getInstance(Context ctx)
{
if (dbhelper == null) {
dbhelper = new DatabaseHelper(ctx);
}
return dbhelper ;
}
My question is: Is this could work?Will it close automatically the DB
and will it leak or throw some exception?
NO it will not close automatically database , when your application will demand for DATABASE object and OS found some of your database instant are alive then Android framework try to connect that object reference (which is probably weak reference )
and i have to say , I don't recommend opening and closing a DATABASE on-demand or temporarily . It is always nice to open the DB up early and keep it open for the duration of your whole activity and close it when the activity gets finished or suspended .

Increment android database version via onclick. (SQLiteOpenHelper)

Is it possible (simple) to get the current database version number, increment it, and pass it to SQLiteOpenHelper's constructor from within an activity rather then hard coding the version number like so?
Activity (called from onclick):
public void updatedb(){
//pseudo-code next 2 comments
//int incVersion = getCurrentDBVersion();<==question here
//incVersion = incVersion + 1;
DatabaseHandler dbincrement = new DatabaseHandler(this, incVersion);
}
SQLiteOpenHelper Extension:
public class DatabaseHandler extends SQLiteOpenHelper{
public DatabaseHandler(Context context, int incVersion) {
super(context, DATABASE_NAME, null, incVersion);
}
}
Yes, use something like this to get the database version:
DatabaseHandler dh = new DatabaseHandler(this);
SQLiteDatabase db = dh.getWriteableDatabase();
int version = db.getVersion();
DatabaseHandler dhInc = new DatabaseHandler(this, version++);
You cannot get the current db version number unless you first open the database. So if your intention was to get the installed database version, increment it, and then call the SQLiteOpenHelper constructor with the new version than I don't believe what Sam proposed will satisfy your request.
Here's why:
1) the first line in a DatabaseHandler constructor must invoke the SQLiteOpenHelper (super) constructor, providing your database version.
2) invoking getWriteableDatabase automatically causes the invocation of the onOpen and/or onUpgrade/onDowngrade methods in your DatabaseHandler. Which onXXXX method is called depends on SQLiteOpenHelper's comparison of the installed database version to the version you provided in your constructor initialization above.
I don't know an easy way to find out the installed database version without providing one first.

Categories

Resources