Database Helper with my upgrade idea:
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "myapp.db";
private static final int DATABASE_VERSION = 11;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) { /* ... */ }
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch(oldVersion) {
case 1: upgradeToSecond(db);
case 2: upgradeToThird();
case 3: upgradeToFourth();
// ...
case 10: upgradeToEleventh();
}
}
private void upgradeToSecond(SQLiteDatabase db) { /* ... */ }
private void upgradeToThird(SQLiteDatabase db) { /* ... */ }
private void upgradeToFourth(SQLiteDatabase db) { /* ... */ }
// ...
private void upgradeToEleventh(SQLiteDatabase db) { /* ... */ }
}
Thanks to the switch without breaks, database schema will be updated step by step to the newest version, no matter what version user had before. If the oldVersion is 8, the upgrade methods upgradeToNinth(db), upgradeToTenth(db) and upgradeToEleventh(db) will be run. Great!
But this code makes an assumption that the value of newVersion is always the newest version of the database (the value supplied to the SQLiteOpenHelper constructor). Is this alwyas true? Are there cases when onUpgrade method is called with newVersion other that the newest one?
But this code makes an assumption that the value of newVersion is always the newest version of the database (the value supplied to the SQLiteOpenHelper constructor). Is this alwyas true?
Yes.
This can be verified by reading the source where onUpgrade() is called with newVersion argument being the same you passed in as the argument to SQLiteOpenHelper constructor.
Are there cases when onUpgrade method is called with newVersion other that the newest one?
onUpgrade execute only after you release the update version of the app and increase the Database Version.
Related
I am trying to updgrade the sqlite version number from 1 to 2 but onupgrade method is not getting called.
do i have to delete application in the device and then install the application to test it ?
Only method which get called is DatabaseHelper and onCreate
No other method get called.
In DatabaseHelper.java file
private static final int DBVersion = 1; // I had change this value to 2.but it is not working.
public DatabaseHelper(Context context, CursorFactory cf) {
super(context, DBName, cf, DBVersion);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TABLE_CREATE_Table);
}
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
//This is not called.If it called i will add the changed here
}
another class for dataprovider.java
#Override
public boolean onCreate() {
dbHelper = new DatabaseHelper(getContext(), null);
return true;
}
using :-
#Override
public boolean onCreate() {
dbHelper = new DatabaseHelper(getContext(), null);
SqliteDatabase db = dbHelper.getWritableDatabase(); //<<<<<<<<<<<
return true;
}
attempts to open the database and thus onCreate/onUpgrade would be called.
- onCreate only if the database does not exist.
- onUpgrade only if the database exists AND the specified version number is greater than the database version stored in the database.
That is when instantiating the Database Helper (subclass of SQLiteOpenHelper) no attempt is made to open the database.
The attempt to open the database is only made when the SQLiteDatabase's getWritableDatabase (or getReadableDatabase) are called. Both attempt to open the database. Noting that getWritableDatabase or getReadableDatabase may well be called implicitly.
Note the above does not include directly using the SQliteDatabase's OPEN methods.
Alternative Fix
I personally tend to force the open when constructing the database helper by using :-
public DatabaseHelper(Context context, CursorFactory cf) {
super(context, DBName, cf, DBVersion);
this.getWritableDatabase();
}
I tend to save the returned SQLiteDatabase into a class variable and then use that rather than using this.getWritableDatabase() in the underlying methods.
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.
I'm using an extension of the SQLiteOpenHelper. My understanding is that onCreate only runs when the requested database does not exist. onOpen should run every time a database is opened.
This seems to work between activities within my app - I need to instantiate a new instance of the database helper in each activity, and onOpen is run but not onCreate. However each time I restart the app, onCreate is being called. Am I misunderstanding how this should work or did I simply implement it wrong?
Here's my helper class:
public class DBWrapper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "MyTest.db";
private static final int DATABASE_VERSION = 2;
public DBWrapper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.d("SQLite DBWrapper","OnCreate run");
ver1(db);
ver2(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d("SQLite DBWrapper","OnUpgrade run; oldVer: " + oldVersion + "; newVer: " + newVersion);
if (oldVersion < 2) {ver2(db);}
}
#Override
public void onOpen(SQLiteDatabase db) {
Log.d("SQLite DBWrapper","OnOpen run");
}
private void ver1(SQLiteDatabase db) {
Log.d("SQLite Wrapper","Creating new Version 1");
db.execSQL("create table UserList (ID integer primary key, Name String, State String, Email String, Status String);");
db.execSQL("create table Contacts (ContactID integer, Display_Name String, Email_Address String, IsPrimary integer);");
}
private void ver2 (SQLiteDatabase db) {
Log.d("SQLite Wrapper","Updating to Version 2");
db.execSQL("create table ActivityRecord (UserID String, ActID String, ActDate date, Rating REAL, Comment String, RateDate date, primary key(UserID,ActID));");
}
//Other methods for inserting and retrieving data
}
This is how I create an instance of the helper in each activity:
private DBWrapper DBHelper;
private SQLiteDatabase db;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
DBHelper = new DBWrapper(this);
db = DBHelper.getWritableDatabase();
}
My logcat is indicating every time I restart my app (in debug mode at least) the onCreate is being called (along with ver1 and ver2). What have I done wrong?
UPDATE: I added more logging and found that A) This is happening on both release and debug variants; B) when I logged db.getVersion() in onCreate, it reported "0". So that either means it forgot the version number when I closed and reopened the app or the db was deleted when the app closed.
Dont create instance of DBWrapper everytime you launch an activity.Just create it once when your application instance is created and use the same instance across all activities of app.See if you still notice the problem.
I doubt multiple instances of DBWrapper are being created in your application(Although multiple instances should have worked logically).
Refer code below:
public class MyApplication extends Application {
DBWrapper mDbwrapper;
#Override
public void onCreate() {
super.onCreate();
mDbwrapper=new DBWrapper();//Share this object with all the activities of the class
}
}
Add this line to manifest:
<application android:icon="#drawable/icon" android:label="#string/app_name" android:name="MyApplication">
I'm using the SQLiteOpenHelper (shown below) in all my apps quiet happily. Lots of upgrades to existing databases ended successful in many year.
This time I need to upgrade the database in one of my apps and this step will last for some time. So I need to put these upgrade statements in their own thread.
What's the best place to do so?
Any help is highly appreciated.
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
protected static final Object lock = new Object();
private static final int DATABASE_NAME = "mydatabase.db";
private static final int DATABASE_VERSION = 3;
private Context context;
private SQLiteDatabase database;
public MySQLiteOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.context = context;
}
#Override
public void onCreate(SQLiteDatabase database) {
synchronized (lock) {
this.database = database;
// Create database statements
}
}
#Override
public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion) {
synchronized (lock) {
this.database = database;
switch (newVersion) {
case DATABASE_VERSION:
switch (oldVersion) {
case 1:
upgradeFrom1To2();
case 2:
upgradeFrom2To3();
}
break;
}
}
}
private void upgradeFrom1To2() {
// Upgrade database statements
}
private void upgradeFrom2To3() {
// Upgrade database statements
}
}
I'd put the code in an IntentService, which is an easy way to implement a background thread for a long-running operation, especially one that is saving data. Use broadcast Intent to send status from the IntentService to other components, and BroadcastReceiver to receive these Intent, if you need to.
I would really avoid doing this in AsyncTask within an Activity; there's too much risk that the operation would be killed. Anyway, AsyncTask is much more complicated than IntentService.
One note: IntentService doesn't persist anything, including its class fields. To protect yourself, you may want to store state in SharedPreferences.
I wrote some code to ensure that my database will be updated properly when I will release updates to my application.
The problem is that the OnUpdate() function of the SQLiteOpenHelper is never called.
Here is the code I wrote in the main activity -
SharedPreferences DB_ver = getSharedPreferences(PREFS_NAME, 0);
myDbHelper = new DataBaseHelper(con, DB_ver.getInt("DB_ver", 1));
try {
if(DB_ver.getInt("DB_ver", 1) !=getPackageManager().getPackageInfo(getPackageName(), 0).versionCode )
{
SharedPreferences.Editor editor = DB_ver.edit();
editor.putInt("DB_ver", getPackageManager().getPackageInfo(getPackageName(), 0).versionCode);
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
Here is the constructor of SQLiteOpenHelper(which extends SQLiteOpenHelper) -
public DataBaseHelper(Context context,int ver_code) {
super(context, DB_NAME, null, ver_code);
this.myContext = context;
}
Now I understood that the Super line is supposed to call the onUpgrade() function automatically, but it doesn't.
I've tested the function onUpgrade() separately, and it works.
Does anyone know what's the problem?
Thanks!
What your doing is really not neccessary. SQLiteOpenHelper does everything you need. Here's a possible scenario. SQLiteOpenHelper has a getVersion() method in case you need to query it at one point (I never did):
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
private static final String dbname = "whatever";
private static final int dbversion = 1; // your first version
//private static final int dbversion = 2; // your second version
//private static final int dbversion = 3; // your third version
public MySQLiteOpenHelper(Context context) {
super(context, dbname, null, dbversion);
this.context = context;
}
#Override
public void onCreate(SQLiteDatabase sqliteDatabase) {
// ... Create first database content
}
#Override
public void onUpgrade(SQLiteDatabase sqliteDatabase, int oldVersion, int newVersion) {
switch (newVersion) {
case dbversion: // suppose your on third version
if (oldVersion == 1) {
upgradeFrom1To2(sqliteDatabase);
upgradeFrom2To3(sqliteDatabase);
}
if (oldVersion == 2) {
upgradeFrom2To3(sqliteDatabase);
}
break;
default:
break;
}
}
public void upgradeFrom1To2(SQLiteDatabase sqliteDatabase) {
// ...
}
public void upgradeFrom2To3(SQLiteDatabase sqliteDatabase) {
// ...
}
}
Two things:
You're not calling editor.commit().
You're creating the database with an initial version value of 1 in that code. Unless you're changing the version number in the AndroidManifest.xml it will never be anything but 1. Until that version changes onUpgrade() doesn't need to be called. onCreate() will be called when the database is first created, but onUpgrade() is only called if the reported version becomes different.
You should change the integer "VERSION" to get your onUpgrade called.
Also, the onUpgrade receive two integers, the first one, is the current version of the database(upgrading from), the second is the version you are upgrading to.
One thing I see is that you're not commiting your changes to the SharedPreferences that you're opening. You need to call editor.commit(); to save changes to SharedPreferences.
Also, have you tried actually opening the database in either read or write mode?