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?
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.
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.
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.
On my app I make use of two datatabases.
This is the class that handles the database management and all the query that are made to it.
public class Database {
private DbHelper DBHelper;
private final Context Context;
private SQLiteDatabase MyDBone, MyDBtwo;
static Context ctx;
private static class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context, String dbName, int dbVersion) {
super(context, dbName, null, dbVersion);
}
#Override
public void onCreate(SQLiteDatabase db) {
// This is where the two databases are created
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVesion) {
// database upgrades are handled here
}
}
}
// database constructor
public Database(Context c) {
Context = c;
ctx = c;
}
// database open
public Database open() throws SQLException {
DBHelper = new DbHelper(Context, BD_NAME, BD_VERSION);
// I have here some if code to decide witch one of the bellow is used
if{
MyDBone = DBHelper.getWritableDatabase();
} else{
MyDBtwo = DBHelper.getWritableDatabase();
}
return this;
}
// database close
public void close() {
DBHelper.close();
}
public Cursor getData(........) {
// My querys are made here
}
}
My problem is that the databases are too big. In the onCreate method I'm getting the error: The code of method onCreate(SQLiteDatabase) is exceeding the 65535bytes limit. On the other side, my app is getting very big on size.
I would like to know what's the best way to address this issue since I can't change my databases.
Since my app must be run offline I can't make query's on a webserver.
I beleive that the best aproach would be to, on the first run of the app, download the databases from somewhere on the internet (drive, dropbox or other side) but since my programming skils are a little green I must pospone this to a must do in the future.
Is it possible, maintaining my Database class, prepack the apk with the databases and install them on the sdcard? On the other side this will increase the apk size (the total of the databases is 15 mb).
Please advise on the best way to address this issue.
Regards,
favolas
I'm trying to get a better understanding of the SQLiteOpenHelper class and how and when onCreate and onUpgrade are called.
My problem is that each time I quit and start my application (technically it's actually each time I create a new instance of MyDB), onCreate is called, and all of the data from the previous usage is effectively wiped... WTF???
I got around this problem initially by creating a singleton MyDBFactory where I created a single instance of MyDB. This allowed the data to be persistent while the application is running at least.
What I would like is for my database schema and data to be persistent!
I basically have:
public class MyDB extends SQLiteOpenHelper{
private static int VERSION = 1;
...
public ContactControlDB(Context context) {
super(context, null, null, VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(DATABASE_CREATE);
db.execSQL(INSERT_DATA);
} catch (SQLException ex) {
ex.printStackTrace();
}
}
and:
public class MyDBFactory{
private static MyDB db;
public static MyDB getInstance(Context context) {
if(db == null) {
db = new MyDB (context);
}
return db;
}
}
What I'd like to know is why onCreate is called every time I have 'new MyDB(context)', and where my database goes each time my app exits.
If you have some appropriate links, or some knowledge that would clue me up a bit, I'd greatly appreciate it!
the line:
super(context, null, null, VERSION);
the second parameter is null specifying that the database should be created in memory, you should give it a name.
Android reference