Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is onUpgrade() not being invoked on Android sqlite database?

Tags:

android

sqlite

I want to upgrade my database when it is installed onto my android emulator. I have set the db version in my DbHelper which inherits from SQLiteOpenHelper to +1.

However, when my 1st activity loads, I instantiate my DbHelper, which I would expect SQLiteOpenHelper to call onUpgrade as the db version is now newer. However it is never called. I'm wondering if there is something I am missing. Where is the version that the DbHelper is using stored to compare against the new version? Why is this not working?

I am actually copying the database from the assets folder into the data folder rather than re-creating the schema.

public class DbHelper extends SQLiteOpenHelper {
    private static final String TAG = "DbHelper";

    static final String DB_NAME = "caddata.sqlite";
    static final int DB_VERSION = 4;

    private static String DB_PATH = "";
    private Context myContext;
    private SQLiteDatabase myDataBase;

    public DbHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        this.myContext = context;

        DB_PATH = "/data/data/"
                + context.getApplicationContext().getPackageName()
                + "/databases/";            
    }

    public DbHelper open() throws SQLException {        
        myDataBase =  getWritableDatabase();

        Log.d(TAG, "DbHelper Opening Version: " +  this.myDataBase.getVersion());
        return this;
    }

    @Override
    public synchronized void close() {

        if (myDataBase != null)
            myDataBase.close();

        super.close();

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.d(TAG, "onCreate called");

        try {           
            createDataBase();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if ( newVersion > oldVersion)
        {
            Log.d(TAG, "New database version exists for upgrade.");         
            try {
                Log.d(TAG, "Copying database...");
                copyDataBase();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }       
        }
    }

    public void createDataBase() throws IOException {

        boolean dbExist = checkDataBase();

        if (!dbExist) {         

            try {
                copyDataBase();
            } catch (IOException e) {
                throw new Error("Error copying database");
            }
        }

        openDataBaseForRead();
    }


    private boolean checkDataBase() {

        SQLiteDatabase checkDB = null;

        try {
            String myPath = DB_PATH + DB_NAME;
            checkDB = SQLiteDatabase.openDatabase(myPath, null,
                    SQLiteDatabase.OPEN_READONLY
                            | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
            Log.d(TAG, "db exists");
        } catch (SQLiteException e) {
            // database does't exist yet.
            Log.d(TAG, "db doesn't exist");

        }

        if (checkDB != null) {
            checkDB.close();            
        }

        return checkDB != null ? true : false;
    }


    private void copyDataBase() throws IOException {

        // Open your local db as the input stream
        InputStream myInput = myContext.getAssets().open(DB_NAME);

        // Path to the just created empty db
        String outFileName = DB_PATH + DB_NAME;

        // Open the empty db as the output stream
        OutputStream myOutput = new FileOutputStream(outFileName);

        // transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[2048];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }

        // Close the streams
        myOutput.flush();
        myOutput.close();
        myInput.close();

        myDataBase.setVersion(DB_VERSION);
    }

    public void openDataBaseForRead() throws SQLException {

        // Open the database
        String myPath = DB_PATH + DB_NAME;      
        myDataBase = SQLiteDatabase.openDatabase(myPath, null,  SQLiteDatabase.OPEN_READONLY);
    }

    public void openDataBaseForWrite() throws SQLException {

        // Open the database
        String myPath = DB_PATH + DB_NAME;      
        myDataBase = SQLiteDatabase.openDatabase(myPath, null,  SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.NO_LOCALIZED_COLLATORS );
    }


}
like image 954
jaffa Avatar asked Oct 04 '11 12:10

jaffa


3 Answers

This is a code snippet from the source of SQLiteOpenHelper.getWritableDatabase():

int version = db.getVersion();
if (version != mNewVersion) {
    db.beginTransaction();
    try {
        if (version == 0) {
            onCreate(db);
        } else {
            if (version > mNewVersion) {
                onDowngrade(db, version, mNewVersion);
            } else {
                onUpgrade(db, version, mNewVersion);
            }
        }
        db.setVersion(mNewVersion);
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
}

onOpen(db);

As you can see, the onCreate() or onUpgrade() are called within the call to getWritableDatabase(). You have to use these calls whenever you need an instance of SQLiteDatabase. You shouldn't use your own methods, except if they are wrappers arround the getWritableDatabase or getReadableDatabase method.

like image 196
kdehairy Avatar answered Oct 18 '22 02:10

kdehairy


Documentation says:

The database is not actually created or opened until one of getWritableDatabase() or getReadableDatabase() is called.

I see that you've implemented your own methods for getting database: openDataBaseForRead() and openDataBaseForWrite(). That's bad ;-)

like image 22
pawelzieba Avatar answered Oct 18 '22 02:10

pawelzieba


I have figured out the problem. This problem comes only when you copy the db from assets. The db in the assets folder has a default version 0. So when you call the database open helper with the version number(say 1) and then overwrite it with the one in assets the version number is reset to 0.

And when you call the db.getReadableDatabase() or db.getWriteableDatabase which is supposed to call the onUpgrade method, it fails because the version number 0 which is supposed to be a fresh database. you can look the source code of SqliteOpenHelper. It triggers the the onUpgrade method only when the version is greater than zero and the current version is greater than the old one.

`

db.beginTransaction();
try {
  //Skips updating in your case
  if (version == 0) {
  onCreate(db);
  } else {
  if (version > mNewVersion) {
    onDowngrade(db, version, mNewVersion);
  } else {
   onUpgrade(db, version, mNewVersion);
  }
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
   db.endTransaction();
}

`

So this is what I did to overcome this issue. Its no where a perfect solution, but it works. Basically you need to update the db version before you call the super method of SQLiteOpenHelper class (your Database manager's constructor).

`

try
 {
   String myPath = MyApplication.context.getDatabasePath(DATABASE_NAME).toString();
   //open a database directly without Sqliteopenhelper 
   SQLiteDatabase myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
   //if the version is default 0 the update the version
   if (myDataBase.getVersion() == 0)
    {
     //update the database version to the previous one
     myDataBase.execSQL("PRAGMA user_version = " + 1);
    }
    //Close DataBase 
    myDataBase.close();
  }
   catch (Exception e)
   {
     //Do Nothing a fresh install happened
   }

`

P.S: I know that the answer is late but, it might help for someone like me who was searching for this particular solution

like image 36
Build3r Avatar answered Oct 18 '22 02:10

Build3r