Reputation: 499
In my application the user can export and import his database file for backup the data.I updated the database version and added some more new columns. now, probebly the user backup the old database of version 1. and after he will try to import he is going to get something like "(1) no such column: (And the Column name here)". How can i call the onUpgrade on old database after import? Can i call onUpgrade on the loaded databae manually? How can i solve it? Do i need to tell the users after they update the application to do fresh backup again for safe? Any idea anyone?
the code how i do import:
// importing database
public boolean importDB(String path , File dbFile) {
try {
boolean isValid = checkDbIsValid(dbFile);
if(!isValid){
Toast.makeText(context, context.getResources().getString(R.string.errorFile),Toast.LENGTH_LONG).show();
return false;
}
//Last loaded file name will be the auto backup file destination
int lastIndexOf = path.lastIndexOf('/');
String loadedFileName = path.substring(lastIndexOf + 1);
new MySharedPreferences(context).putStringLastFileNameSent(loadedFileName);
FileInputStream fis = new FileInputStream(path);
//Destination to:
String outFileName = DATABASE_DIRECTORY + DbContract.DB_NAME;
OutputStream output = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
// Close the streams
output.flush();
output.close();
fis.close();
Toast.makeText(context, context.getResources().getString(R.string.importDatabaseSucceed),Toast.LENGTH_LONG).show();
return true;
} catch (Exception e) {
Toast.makeText(context,context.getResources().getString(R.string.errorImportDatabase),Toast.LENGTH_LONG).show();
e.printStackTrace();
return false;
}
}
And this How i check if the datbase file is valid:
public static boolean checkDbIsValid(File db) {
SQLiteDatabase sqlDb;
Cursor cursor;
try {
sqlDb = SQLiteDatabase.openDatabase (db.getPath(), null, SQLiteDatabase.CONFLICT_NONE);
cursor = sqlDb.query(true,DbContract.TABLE_DAY, null, null, null, null, null, null, null);
for( String s : DbContract.ALL_COLUMN_KEYS_IN_TABLE_DAY){
cursor.getColumnIndexOrThrow(s);
}
//This make sure the user can import old database on new Version of table
try {
cursor = sqlDb.query(true,DbContract.TABLE_SETTING,null, null, null, null, null, null, null);
for( String s : DbContract.ALL_COLUMN_KEYS_IN_TABLE_SETTING_v2){
cursor.getColumnIndexOrThrow(s);
}
System.out.println("DATABASE LOADED FROM V2");
}catch (Exception fileNotV1){
System.out.println("ERROR FILE .. IS NOT V2");
try {
for( String s : DbContract.ALL_COLUMN_KEYS_IN_TABLE_SETTING_v1){
cursor.getColumnIndexOrThrow(s);
}
System.out.println("DATABASE LOADED FROM V1");
}catch (Exception fileNotV2){
System.out.println("ERROR FILE .. IS NOT V1");
return false;
}
}
if(DbContract.VERSION<sqlDb.getVersion() ){
return false;
}
sqlDb.close();
cursor.close();
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
} catch (SQLiteException e) {
e.printStackTrace();
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
public class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context) {
super(context, DbContract.DB_NAME, null, DbContract.VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DbContract.CREATE_TABLE_WORKS);
db.execSQL(DbContract.CREATE_TABLE_SETTING);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 1) {
updateToVersion2(db);
}
}
public void updateToVersion2(SQLiteDatabase db){
db.execSQL("ALTER TABLE " + DbContract.TABLE_SETTING + " ADD "
+ DbContract.COLUMN1 + " REAL DEFAULT 1");
db.execSQL("ALTER TABLE " + DbContract.TABLE_SETTING + " ADD "
+ DbContract.COLUMN0 + " REAL DEFAULT 0");
db.execSQL("ALTER TABLE " + DbContract.TABLE_SETTING + " ADD "
+ DbContract.COLUMN2 + " REAL DEFAULT 0");
}
}
Upvotes: 1
Views: 479
Reputation: 38595
If you are literally copying the db file to and from storage, then in theory when you call getReadableDatabase()
or getWriteableDatabase()
, it should check the database version and go through the typical upgrade flow, which calls onUpgrade()
and gives you both the old and new version numbers. If that's the case, and if you are properly implementing onUpgrade()
, then things should "just work".
If the upgrade isn't happening automatically, after opening the database you can check the database version yourself with getVersion()
and perform any commands you like to upgrade the schema manually.
EDIT
In order for onUpgrade()
to run automatically, you need to open the database using your DbHelper
class. Absent that, you need an instance of DbHelper
so that you can call its onUpgrade()
method and pass it the SQLiteDatabase
you opened manually.
SQLiteDatabase sqlDb = ...;
DbHelper dbHelper = ...;
dbHelper.onUpgrade(sqlDb, sqlDb.getVersion(), DbContract.VERSION);
I'm not particularly fond of your method to check if the DB file is valid. You would have to keep track of every schema version in your contract and check each one; that seems unwieldy. What if instead you copy the file into a temp file in your app's database directory, open it with a DbHelper
(which should try to open it), and if anything fails you can abort and delete the file? If it succeeds, you can just rename the file and reopen the database. That's probably how I would approach it.
Upvotes: 1