In my Android application I'm using the SQLite DB with SQLiteOpenHelper
I have several DB versions, and I've been doing onUpgrade() operations by switching on the old db version, but now I have to remove one of the tables because I'm no longer using it, so should I do something different?
public void onCreate(SQLiteDatabase database) {
database.execSQL(tableUserCreate);
database.execSQL(tableProductsCreate);
database.execSQL(tablePicturesCreate);
}
public void onUpgrade(SQLiteDatabase database, int version_old, int current_version) {
switch (version_old) {
case 1:
database.execSQL(addPostcodeFieldToUserTable);
database.execSQL(tablePlacesCreate);
// Intentional fallthrough, no break;
case 2:
database.execSQL(tableProductVideosCreate);
break;
}
} // End of onUpgrade
Now I want to remove the User table in a new DB version. What do I do?
SQL for dropping a table:
DROP TABLE table_name
Or use:
DROP TABLE IF EXISTS table_name
When you change Database_Version it called
onUpdate(SQLiteDatabase db, int oldVersion, int newVersion)
method first, so you have to do all drop or create kind of stuff inside that method. In your case you use Switch case i do same things using if else.
But both work fine in that case.
if (newVersion == yourNewVersion) {
try {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS " + YOUR_TABLE_NAME);
// Create tables again IF YOU WANT TO CREATE HERE
} catch (Exception e) {
e.printStackTrace();
}
Related
i have a android app of version 4.20 inside 10M sqlitedb, now i have a app of version 4.21 inside 100M sqlitedb,i want upgrade 4.20 to 4.21, when it upgrade have completed, i find the sqlite size still keep 10M in 4.21 app.
so the question is how to override sqlitedb when upgrade app 4.20 -> 4.21 。
My business is: app will have a lot of data synchronization. In order to optimize, every app will fill in initialization data into SQLite when it is published. Therefore, after each app have upgraded, I hope to completely overwrite SQLite。
any help is very apprecated.
You can upgrade your database version and in the onUpgrade() method of your SQLiteOpenHelper drop all tables and call onCreate() method for recreat database.
public class MyOpenHelper extends SQLiteOpenHelper {
...
private void resetDB(SQLiteDatabase db) {
// Drop all tables
db.execSQL("DROP TABLE IF EXISTS table_name");
// TODO duplicate above line for other tables
// Crate tables again
onCreate(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
resetDB(db);
}
#Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
super.onDowngrade(db, oldVersion, newVersion);
resetDB(db);
}
}
The Problem is the onUpgrade function is not being called because when you load a database from assets the the default version is 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.
If you see the SqliteOpenHelper source code
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();
}
When version number of db is is 0 the onUpgrade function is not called.
One way to fix the problem is change the version number of db when you add it to the asset or change it before opening
The solution change the version number and your problem
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
}
I updated my application today and started seeing the following in crashlitics:
android.database.sqlite.SQLiteException: no such table: LESSONATTACHMENT (code 1 SQLITE_ERROR[1]): , while compiling: SELECT _THUMB_PATH FROM LESSONATTACHMENT WHERE _id = 2
I create a new table inside my SQLiteOpenHelper, it was not in the application before I update.
This is how I created the table:
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_LESSON_ATTACHMENTS);
}
Should I instead place it in OnUpgrade? Like this:
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(CREATE_TABLE_LESSON_ATTACHMENTS);
}
Whenever you want to make changes in tables or create a table on app update, you have to change the version of database. In your case let us suppose you have upgraded from dbversion from 2 to 3 and you want to create a table for versions greater than 2.Then you have to create a table in onUpgrade like below:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(newVersion>=3){
db.execSQL("CREATE TABLE IF NOT EXISTS `TABLE_NAME` ....");
}
}
here I am creating the table for all the versions greater than 2 since there is a possibility that dbversion gets upgraded from 2 to 4 or some other version greater than 3.
You Need to increase your Database Version and create the new Table in onUpgrade and Maybe delete the old Table.
Your Database is created only once. Upgrades are done in the onUpgrade function
You can also delete the Database and it will be created new with the new Table, but it depends if you are able to do this at the Moment.
for example this Code will upgrade your Database up to the current Version
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch(oldVersion){
case 1:
//Changes between version 1 and 2
case 2:
// Changes between Version 2 and 3
case currentversion:
//changes between last change and now
}
}
when you update your database just put a new case with the new Version below this. This way you can maintain every Version of Database and make sure that it will be upgraded properly. Important is, that you don't use a break after each case.
this Code Shows for example one of my Databases, when I increase the Version I simply add a new case for the old Version. When you havent upgraded your Database yet, set the new Version to 1 higher than your old Version and add a case clause for the old Version.
public class database extends SQLiteOpenHelper {
private static String DATABASE = "Database";
private static int VERSION = 4;
public database(#Nullable Context context) {
super(context, DATABASE, null, VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE DATABASE TABLE");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch(oldVersion){
case 1:
db.execSQL("CHANGES DONE AFTER VERSION 1");
case 2:
db.execSQL("CHANGES DONE AFTER VERSION 2");
case 3:
db.execSQL("CHANGES DONE AFTER VERSION 3");
}
}
I have created DatabaseHelper to make my development easier. If my Database version is upgrade from 1 to 2, onUpgrade will called and table will be dropped. I wonder will this cause my data stored in table dropped too? I means, will I lost my data stored in table?
Here's the code.
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}
By looking at your code, Yes you will lose the current table of the database and a new empty table notes will be created.
So the best way to upgrade the database is before this:
db.execSQL("DROP TABLE IF EXISTS notes");
statement, you should made a backup of your current database (easiest way is to rename the table) and then drop the current table to create the new one.
It is always a good programming practice to keep a backup of information before editing/upgrading it.
When you drop a table all it's data is deleted too. If you want to keep it, rename the table first, create the new one and then select the data from the old into the new one.
You can alter table in onUpgrade method if there is change in coloumn. If you want to make fresh table then only call Drop inside onUpgrade, if there is no change in schema dont do anything inside onUpgrade method.
I have seen another question about schema upgrade/migration using green dao (here)
There are lots of links in that answer for a good pattern to use when doing schema upgrades - however there are no examples of what you actually do to your data to migrate it properly and I'm having trouble finding anything.
In my case, my migration is incredibly straight forward - I do not wish to transform any existing data, I simply need to add some new tables to my schema, which I suspect is a fairly common situation.
What is the easiest way to add new tables to your schema without deleting data your users have already saved? A specific example would be greatly appreciated.
It would be awesome if greenDao provided a class similar to DevOpenHelper that would simply add new tables/columns that didn't previously exist in the schema without dropping existing tabes/data first.
I finally had time to dig in to this myself and realized it's quite easy to add a new table while retaining data in old tables.
DISCLAIMER: While I realize this implementation is specific to my scenario, I think it's helpful for someone like me who has used an Android ORM tool (greenDao) exclusively to deal with SQLite on Android. I understand this is pretty common for those of you who have written your own table creation queries from the beginning, but for someone who has been sheltered from the guts of using a SQLite DB with Android, I think this example will be helpful.
ANSWER:
You can either modify the DevOpenHelper inner class or create your own class. I chose to edit DevOpenHelper for the time being to keep my example simple - however, note that if you regenerate your greendao classes, DevOpenHelper will be overwritten. It would be a better idea to create your own class like "MyOpenHelper" and use that instead.
Before my changes, DevOpenHelper.onUpgrade looked like this:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
}
Instead of dropping all tables, take a look at the createAllTables method that is auto-generated by GreenDao.
Rewrite onUpgrade to check if the "oldVersion" is the one you want to upgrade from, then only call the createTable methods for "new" tables. Here is what my onUpgrade method looks like now:
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " +
//Going from older schema to new schema
if(oldVersion == 3 && newVersion == 4)
{
boolean ifNotExists = false;
//Leave old tables alone and only create ones that didn't exist
//in the previous schema
NewTable1Dao.createTable(db, ifNotExists);
NewTable2Dao.createTable(db, ifNotExists);
NewTable3Dao.createTable(db, ifNotExists);
NewTable4Dao.createTable(db, ifNotExists);
}
else
{
dropAllTables(db, true);
onCreate(db);
}
}
Adding a new column would be similar, except you'd have to write some SQL or take a look at the auto-generated SQL create statements from greenDao and leverage those.
To add a single new column (NEW_COLUMN, assuming it's an INTEGER type) to an existing table (EXISTING_TABLE), do the following:
db.execSQL("ALTER TABLE 'EXISTING_TABLE' ADD 'NEW_COLUMN' INTEGER");
For me right now, all I needed to do was add new Tables so this ended up being rather straight forward. Hopefully someone else finds this useful.
I made an slightly different approach to handle the updates automatically no matter where the previous user comes from.
First I created a Class that implements the method onUpgrade on a SQLDatabase
public abstract class AbstractMigratorHelper {
public abstract void onUpgrade(SQLiteDatabase db);
}
From this class will inherit all the migrators helpers I will declare afterwards
I will write an example of one of them
public class DBMigrationHelper5 extends AbstractMigratorHelper {
/* Upgrade from DB schema x to schema x+1 */
public void onUpgrade(SQLiteDatabase db) {
//Example sql statement
db.execSQL("ALTER TABLE user ADD COLUMN USERNAME TEXT");
}
}
After this you need to implement the logic on the class that is actually called on upgrade, where you will need to remove the previous DevOpenHelper for a custom one that could look like this
public static class UpgradeHelper extends OpenHelper {
public UpgradeHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory);
}
/**
* Here is where the calls to upgrade are executed
*/
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
/* i represent the version where the user is now and the class named with this number implies that is upgrading from i to i++ schema */
for (int i = oldVersion; i < newVersion; i++) {
try {
/* New instance of the class that migrates from i version to i++ version named DBMigratorHelper{version that the db has on this moment} */
AbstractMigratorHelper migratorHelper = (AbstractMigratorHelper) Class.forName("com.nameofyourpackage.persistence.MigrationHelpers.DBMigrationHelper" + i).newInstance();
if (migratorHelper != null) {
/* Upgrade de db */
migratorHelper.onUpgrade(db);
}
} catch (ClassNotFoundException | ClassCastException | IllegalAccessException | InstantiationException e) {
Log.e(TAG, "Could not migrate from schema from schema: " + i + " to " + i++);
/* If something fail prevent the DB to be updated to future version if the previous version has not been upgraded successfully */
break;
}
}
}
}
So if you are careful naming your Migration Helpers (i.e. MigrationHelper5 does the migration from schema 5 to schema 6) you can implement this logic and then in every MigratorHelper class just implement the execSQL call with all the sql code that you need to implement.
Finally one more remark, if you are working with proguard, the method find name by class might not work, since class names are changed when obfuscating the code. You might want to consider add an exception on the proguard configuration file (proguard-rules.pro) to exclude any class that extend from AbstractMigratorHelper
# Avoid errors when upgrading database migrators
-keep public class * extends yourpackage.locationofyourclass.AbstractMigratorHelper
I do it a slightly different way.
I add my new #DatabaseTable classes and any #DatabaseFields to existing #DatabaseTable classes and run DatabaseConfigUtil.
Then I'll add a new method to my DatabaseUpgrader class and modify my DatabaseHelper, changing the DATABASE_VERSION value and the onUpdate method
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static final int DATABASE_VERSION = 3;
#Override
public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
if (newVersion > oldVersion) {
switch (oldVersion) {
case 1:
DatabaseUpdater.from1to2(connectionSource);
DatabaseUpdater.from2to3(connectionSource);
break;
case 2:
DatabaseUpdater.from2to3(connectionSource);
break;
default:
onCreate(db);
}
}
}
public static DatabaseHelper getInstance() {
return DatabaseHelper.mHelper;
}
public static void setInstance(Context context) {
DatabaseHelper.mHelper = new DatabaseHelper(context);
}
…
}
And then in the DatabaseUpdater class
public class DatabaseUpdater {
private static final String TAG = "DatabaseHelper";
public static void from1to2(ConnectionSource connectionSource) {
try {
DatabaseHelper helper = DatabaseHelper.getInstance();
//Example add a table
TableUtils.createTable(connectionSource, AnotherEntity.class);
} catch (SQLException e) {
Log.e(TAG, "Error upgrading database to v2: ", e);
} catch (java.sql.SQLException e) {
e.printStackTrace();
}
}
public static void from2to3(ConnectionSource connectionSource) {
try {
DatabaseHelper helper = DatabaseHelper.getInstance();
//Example add a field to a table
RuntimeExceptionDao<MyEntity, Integer> myDao = helper.getMyDao();
diaryDao.executeRaw("ALTER TABLE myEntity ADD firstNewField");
diaryDao.executeRaw("ALTER TABLE myEntity ADD anotherNewField");
} catch (SQLException e) {
Log.e(TAG, "Error upgrading database to v3: ", e);
}
}
}
To answer question posted by #MBH on first answer. Also I did not find the answer in this post hence adding.
GreenDAO uses the schema version number from build.gradle file. Gradle file should contain below
android {
...
}
greendao {
schemaVersion 1
}
Refer this link for more information. Then on upgrade change this number to 2 or any increment. Based on that number GreenDAO calls below API from android.database.sqlite.SQLiteDatabase.DatabaseOpenHelper.java
public DatabaseOpenHelper(Context context, String name, int version)
As standard approach by Sqlite DB upgrade, it calls below API
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
As other answers suggest, this method can be overridden in derived class and can handle any project specific upgrades.
Hope this helps.
I want the sqlite database of my app to be cleared each time the application is updated.
To do that, I make a drop table query on all my tables, in the "onUpgrade" function of SQLiteDatabase.
I got 2 problems:
- at the first launch of my app, I don't do any special.
- at the second launch, I add a "setVersion(2)" line. It calls the onUpgrade method but the logs are strange:
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
Log.d("GDB", "onUpgrade "+oldVersion+" -> "+newVersion);
}
----------------------------------------------------------
DEBUG/GDB(5928): onUpgrade 2 -> 1
So when I make a setVersion(), the 2 versions seems to be switched.....
My second problem is that is I launch my app a third time, without changing the code ( so the setVersion(2) is already here), the onUpgrade method is called again! Did I miss something to definitively set the version to 2?
I don't think you should be setting the version of the database in code directly using the setVersion method. Instead you should pass the schema version into the constructor of your SQLiteOpenHelper (or at least your class which extends this). Your onUpgrade method should then contain condition statements to decide what to run depending on what version the user is upgrading from. These conditions should form a cascade so that coming from a low version applies in sequence all database updates required to get the user to the current level.
So when you want to change your schema, you add a new condition to your onUpgrade and up the schema version passed to your constructor.
This is what the constructor in the OpenHelper looks like:
public TiftHelper(Context context) {
super(context, DATABASE_NAME, null, SCHEMA_VERSION);
}
Then the onUpgrade looks something like this:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d(TAG, "on upgrade called. Old version:" + oldVersion
+ ". New version:" + newVersion);
if (oldVersion == 19) {
db.execSQL("create table challenges_temp as select * from challenges;");
db.execSQL("drop table challenges;");
db.execSQL(create_challenges);
db.execSQL("insert into challenges (_id, name, is_predef, status) select _id, name, is_predef, 'IN_PROGRESS' from challenges_temp");
db.execSQL("drop table challenges_temp;");
}
if (oldVersion <= 20) {
// adding the status column to the challenges table
db.execSQL("create table challenges_temp as select * from challenges;");
db.execSQL("drop table challenges;");
db.execSQL(create_challenges);
db.execSQL("insert into challenges (_id, name, is_predef, status) select _id, name, is_predef, 'IN_PROGRESS' from challenges_temp");
db.execSQL("drop table challenges_temp;");
}
etc.
That works fine for me.