In my android app, I was using a standard SQLite database with a helper class that had 1 table with 3 columns. In the most recent update I had to add another column of to the table, but some users have reported crashes, which (judging by the stack trace) I think comes from the new version trying to read from a column that does not exist because the data is from the old version. How can I protect the users' data between updates short of a manual backup and restore?
Here is the link to the complete updated database class:
https://github.com/cjbrooks12/scripturememory/blob/working/src/com/caseybrooks/scripturememory/databases/VersesDatabase.java
SQLiteOpenHelper will handle the database versioning, you will just have to provide it with proper database version numbers and overridden callbacks. Looking at your code:
Your DB_VERSION is 1. When you change the database schema between released versions, you should increment this number. The version number is stored in the database file, and if the version provided in code is different from the one stored in file, onUpgrade() or onDowngrade() will be called accordingly. In your case, since the database file already exists, no onCreate() was called and since the version numbers matched, no upgrade was performed.
Your onUpgrade() drops the table and then recreates it. In some cases this might be ok, say, it's just a cached copy of data stored elsewhere, but usually as a user, I don't want an app upgrade to delete my data. Implement onUpgrade() so that it does the necessary schema modifications while preserving data. Some generic strategies for this:
If it's just adding some columns ALTER TABLE and put some suitable default values.
If it's more complex schema change, rename the old tables to temporary names, create new tables and then migrate data from the temp tables.
In any case, after onUpgrade() the database schema should be in the same shape it would be if onCreate() was called to create a new database, but with existing data preserved.
Related
My Android App use SQLite database as an asset.
I deliver a .db file in /assets directory to provide both read-only tables and read/write tables.
When the user uses the App, it's datas are stored in read/write tables.
Sometimes I need to modify the schema of the database by adding a column in a table for example.
So I need to deliver a new .db file in the release.
If i do this the problem is that the user loses his data.
Is there a way to export/import the user data when he updates the App on his devise ?
I didn't find anything about this issue on the Web.
The standard way of handling this is, assuming that you are using (extending) the SQLiteOpenHelper class, is to utilise the version number in conjunction with overriding the onUpgrade method to ALTER the schema of the existing database, thus retaining the existing data.
version int: number of the database (starting at 1); if the database is older, onUpgrade(SQLiteDatabase, int, int) will be used to upgrade the database; if the database is newer, onDowngrade(SQLiteDatabase, int, int) will be used to downgrade the database
The SQLiteOpenHelper (or if using SQliteAssetHelper which extends SQLiteOPenHelper), when opening the database (if it exists) compares the coded version number against the version number stored in the header of the database. If the coded version number is greater than the stored version number then the onUpgrade method is called which is passed three parameters,
the SQLiteDatabase
the old (stored) version number as an int
the new (coded) version number as an int
You would typically check old and new and have code that makes the changes (ALTER TABLE or an alternative if the limitations of the ALTER TABLE force to do something like rename the original table, CREATE the changed table with the correct name, copy the existing data from the original table to the new table and finally DROP the renamed original table )
If the App is installed for a new user then that is when the asset will be copied. Thus covering both scenarios.
If not using an extension of SQLiteOpenHelper, then you can mimic using the stored version number by reading 4 bytes at offset 60 and comparing this to a coded version or even by copying the asset and getting the asset's version number, which would be managed with whatever tool you use to maintain the asset (e.g. via the user_version PRAGMA)
SQLite Database Header
when I created a migration, I inserted some data, but the newly installed app did not perform the migration.
do I need to insert/update data on the onCreate call every time I migrate?
Migrations won't run if you install the app, as no Database exists so it is created.
Migrations are designed to handle new versions of the App being published and specifically new versions that also change the Room database version.
do I need to insert/update data on the onCreate call every time I migrate?
NO! onCreate will not run, it only runs once automatically for the lifetime of the database (unless you call it from the Migration).
For an App to be installed, it cannot exist and thus must be uninstalled, the uninstall deletes all the App's data including the databases.
You should be doing what is required in the Mirgation code and then it depends on what you do. e.g. :-
If adding a column for instance and you use the ALTER TABLE your_table ADD COLUMN the_new_column_definition then the existing data is preserved.
If adding a new table then there is no issue.
If however you wanted to change a column's definition (other than it's name) then you have to drop and recreate the table. What would typically be done would be to create the new table, populate it from the original table, rename or drop the original table and the rename the new table to be the name of the original table.
You may wish to have a look at SQLite ALTER TABLE
I want to update my app at play store with bug fixes. APP already uses Room DB. Do i need to migrate the DB. There is no change in the DB. There are only UI changes. Does new version effect the people DB, who are already using it. Can someone provide the details about this.
Thanks in advance.
There is no change in the DB.
The database remains and you do not need to change the version of the database (as per the #Database( .... version=?)).
You only need to change (increase) the database version if the schema has changed.
If you increase the version number and do not have a migration (migration class to call) to cover that version, then an IllegalStateException will occur unless you have .fallbackToDestructiveMigration() which WILL delete and recreate the database (effectively empty the database with the ne schema).
You can also use fallbackToDestructiveMigrationFrom()
It is the version number that determines if a database migration is required. However, if a change is made and if any of the entities mismatch the database when the .build method is run then an exception will result.
If you change an Entity (the schema) and do not increase the version number you will get an IllegalStateException with the message starting with Room cannot verify the data integrity. This is because the hash stored in the room_master_table has changed.
If you changed the database schema outside of room then you'd get an exception indicating that the expected schema (the database according to the Entities) does not match the schema found (the database according to the actual database).
In your situation I'd suggest not changing the version number.
You may wish to have a look at:-
Migrating Room Databases
#Database
fallbackToDestructiveMigration
fallbackToDestructiveMigrationFrom
I'm trying to figure out the best way to handle database upgrades and versioning.
At the moment I delete the database and log users out when I do a point release, which isn't a great experience.
Can anyone recommend any tips for doing this?
Your database version is independent of your app version. If your database schema doesn't change at all, you shouldn't need to do anything to your database during an update.
When your database schema changes, you should handle database updates in onUpgrade() of your SQLiteOpenHelper. This method is called when you try to access your database and you have updated your database version, as described in the Data Storage Options documentation.
If you are using a third party library to handle your databases, it should either handle the upgrade for you or provide similar functionality.
There is no universal strategy for upgrading your database here. What you do depends completely on what your schema looked like before the upgrade and what the new schema looks like. Depending on what changed, you might create new tables or columns, delete tables or columns, update rows in the database, or move data between tables. If you have a specific question about how to migrate your data, create a new question describing the new and old schemas.
The way we do it is that we run a routine every time the app starts that calls a stored proc on the server to get SQL that upgrades the database if it is necessary. (The sql can be quite involved: dropping tables and recreates them with new structures and inserting new values). We store the version of the database in the database itself and upgrades to the new version.
We don't use the onUpgrade() call.
I have the following scenario:
My app is published with database version 4 to the customers.
I did some bugfixes and added some more features. This process also changed my models and thats why the database changed too.
How can I check what database version the customer has installed on his devices and migrate the old data to the new database? Is there an onUpgrade method or something like this in the ActiveAndroid Library?
After taking a deeper look into ActiveAndroid's sourcecode I found a solution.
ActiveAndroid can use sql-scripts located in your asset folder to migrate from one version to another.
It sorts all your sql files located in assets/migrations/ using a natural sorting algorithm:
Each SQL script which was found will be executed if its > oldVersion and <= newVersion
if you access your db via SQLiteOpenHelper and do proper db versioning than you can use it's onUpgrade method to run some code to update your db. Othewise you should do your custom solution.
I suggest that you create a class which extends SQLiteOpenHelper. When your database is opened through this class, it will automatically call the onUpgrade() method when necessary. The parameters to this method include the new and old version numbers of your database schema.
Whenever your schema changes you need to increment the database version number, either through Configuration or AA_DB_VERSION meta-data. If new classes are added, ActiveAndroid will automatically add them to the database. If you want to change something in an existing table however (e.g. add or delete a column), this is done using sql-scripts named <NewVersion>.sql, where NewVersion is the AA_DB_VERSION, in assets/migrations.
ActiveAndroid will execute a script if its filename is greater than the old database-version and smaller or equal to the new version.
Let’s assume you added a column color to the Items table. You now need to increase AA_DB_VERSION to 2 and provide a script 2.sql.
ALTER TABLE Items ADD COLUMN color INTEGER;