I have an Android app where I am using prepopulated database from assets.
I would like to know what is the best way to do migrations if I would like to keep user's data and also add any new records from prepopulated database if there are any. (I assume I need to compare records in device's database with prepopulated one)
I have tried fallbackToDestructiveMigration, but this method is just removing device's database and copying new one from assets. (So I am loosing user's data)
The standard migration approach is good, but I don't know how to check during migration if there are new records in prepopulated database and add them to the device's database after migration.
Thank you!
The fallbackToDestructiveMigration() method will destructively recreate database tables, ONLY IF the migrations steps that would migrate old database schemas to the latest schema version are not found.
Source: Room docs
In your case, lets say the pre-exisitng version of your DB is 1, and the new one you are going to provide is 2, then your migration steps from 1 to 2 can be as follows:
// Create a table for backup
CREATE TABLE myTable_backup(tableId VARCHAR,....);
// Copy old data to this backup table
INSERT INTO myTable_backup SELECT tableId,... FROM myTable;
// Delete old table
DROP TABLE myTable;
// Create the required table with new schema
CREATE TABLE myTable(tableId VARCHAR,....);
// Insert pre-existing data to your new schema
// Overwrite might / might not happen based on your Primary key strategy
INSERT INTO myTable SELECT tableId,... FROM myTable_backup;
// Delete the backup table
DROP TABLE myTable_backup;
The migration steps can be written as per this Room docs.
Related
We have an Android app, which is using read/ write data using Android Room database library, and then download/ upload to cloud storage.
Now, we are developing an iOS app, which is suppose able to read/ write the data.
There is no issue for our iOS app to read SQLite file written by Android Room database, because we are the one who define database schema.
However, there are issue, for iOS app to write an Android Room database library compatible SQLite file. We notice Android Room database library is expecting the following 3 additional tables.
CREATE TABLE room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT);
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE sqlite_sequence(name,seq);
The sample data contain in the 3 additional tables are as follow.
INSERT INTO "main"."room_master_table" ("id", "identity_hash") VALUES ('42', '5471e2f102feee2750d42986836b0c42');
INSERT INTO "main"."android_metadata" ("locale") VALUES ('en_US');
INSERT INTO "main"."sqlite_sequence" ("name", "seq") VALUES ('plain_note', '62');
INSERT INTO "main"."sqlite_sequence" ("name", "seq") VALUES ('attachment', '6');
INSERT INTO "main"."sqlite_sequence" ("name", "seq") VALUES ('tab_info', '5');
I think I am able to generate data for android_metadata & sqlite_sequence manually.
But, I am clueless in generating data for room_master_table.
I tested a SQLite file without room_master_table, it will cause the following error during reading via Android Room.
Caused by: java.lang.IllegalStateException: Room cannot verify the
data integrity. Looks like you've changed schema but forgot to update
the version number. You can simply fix this by increasing the version
number.
I was wondering, is there a way to generate SQLite file from iOS platform, which is compatible with Android Room database library? Or, it is simply not possible?
In short don't try to create the said tables, they are all system tables you should let them be created accordingly.
android_metadata just stores the locale and is created by the android SQLiteOpenHelper.
room_master_table stores a hash of the schema, if the hash doesn't match then you get Looks like you've changed schema but forgot to update the version number.. The hash is created at compile time.
sqlite_sequence exists if the AUTOINCREMENT keyword is used in a PRIMARY KEY definition. There is rarely any need to use AUTOINCREMENT. It is recommended to not use AUTOINCREMENT (Room's autogenerate = true). The table hold's a row per table that has AUTOINCREMENT. It stores the highest ever used rowid value. When a new row is inserted instead of just using the highest rowid to determine the next ( max(rowid) + 1), it instead uses the greater of the max(rowid) and the stored max ever allocated value and then adds 1. Thus it has to access the sqlite_sequence table to get the value and thus unless there is a need to always have a rowid greater than any ever allocated it is a waste.
If you use an SQLite database for both, and the IOS doesn't have sqlite_sequence then you should not have autogenerate coded anywhere in the #PrimaryKey annotation, as Room will then hit the Expected .... Found .... errors. As room is very particular about the schema.
I was wondering, is there a way to generate SQLite file from iOS platform, which is compatible with Android Room database library? Or, it is simply not possible?
Yes it is possible. In short don't create any of the tables, let Room create them. Then (assuming that both schemas are similar enough (data wise) have the same columns and column order). You can then load the Room tables from the SQLite file along the lines of
`INSERT INTO room_table_? SELECT * FROM ios_table_?;`
When and how frequently you do this will change exactly how you get to the stage when you can do the copy. If the schemas are identical and it's a one of then you could perhaps utilise the .createFromFile or if the schemas are close .createFromFile where you can modify the schema accordingly (if so then you might find this useful).
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
Imagine I deliver a prepopulated database with my application version 1.
The prepopulated database includes a table "my_items" with the column item:
item
------
apple
For application version 2 I deliver an updated prepopulated database, so table "my_items" includes
item
------
apple
milk
How would one make sure that the new prepopulated data is transfered to the database of the app in a non-destructive way? (user is allowed to insert new items to "my_items" by himself and I need to keep his input). I read the Room migrations documentation, but it seems like the prepopulated data is only read on a destructive migration, which is not what I want, as the items entered by the user himself shall remain available.
Room's built in migration processing does not support the merging of existing data into a new prepopulated database.
An approach that should work is to give the prepackaged database file for each app version a unique name, e.g. my_items1.db, my_items2.db, etc. In app version 2, create the main database from prepackaged database file my_items2.db. Then open the database for the previous version as a separate Room database instance, query it to find the items added by the user, and insert those into the new main database with appropriate default values for new fields. This merge processing could be triggered by the onCreate() callback for the main database. The merge processing should be done in a transaction. If it completes successfully, you can close and delete the database for the previous version. In the event of failure, you'll need to notify the user and implement some sort of mitigation processing.
i have developed a tutorial app in which i have saved more than 500 questions and answers.
their is one more table called favourites. which is for user input.
now, i want to update my app with new questions and answers.
but i dont want to erase the data of favourites table (in case, user has marked some questions favourites, so those questions should not be erased from favourites)
so how can i do it?
because, i have used SQLassethelper library for database connectivity.
my old db contains:
data table(static table)
favourites table(local table)
so, according to sqliteassethelper documentation i added my new db:
that contains: updated data table. i didnt inserted favourites table here coz it will be created in script file.
and stored that db in assets>>databases folder.
then
i created a script fild
db.db_upgrade_1-2.sql
alter table "favourites" rename to "favourites_tmp";
create table "favourites" (
"id" Integer not null primary key autoincrement unique,
"question" text,
"answer" text,
"category" text,
"catid" integer
);
insert into "favourites" ("id","question","answer","category","catid") select from "favourites_tmp" "id","question","answer","category","catid" from "favourites_tmp";
drop table "favourites_tmp";
so i think here favourites table will be created with old data.
but
when i run the project, it says: no such tabld favourites.
The documentation tells you to
create a text file containing all required SQL commands to upgrade the database from its previous version to it's current version.
You can use any SQL commands, not only ALTER TABLE, but also INSERT.
The database file in the assets folder is used only if there is no old data (if the app is installed for the first time). If there is old data, SQLiteAssetHelper executes the SQL upgrade script instead.
The SQLiteAssetHelper project contains an example that shows how such a script would look like.
To keep the data in the favourites table, you do not need to do anything.
To add the new question/answers, use a bunch of INSERT statements.
(For how to get those INSERT statements, see How to compare two SQLite databases.)
finally i got answer for my questions.
as i said i am using SQLiteAssetHelper library.
and now i want to add new records and want to release the update version.
so i was finding the feature by which i can store new updated db in assets folder. and this library will update the old db in user's phone. with keeping particular table in old db.
but currently this library doesnt offers such feature.
the upgrade script can be use only to make changes is old db. we can add new updated db and copy the data from new one.
if we want to add new data and also dont want to erase local data table.
then we need to write insert queries in upgrade script as described in documentation of that library.
but if we have to add 100 queries then their is no shortcut for it.
we have to write 100 insert statements in upgrade script.
and in case if we gate a error saying
cannot upgrade read only database from version x to y then
here is the solution for it:
SQLITE cant upgrade read-only database from version 1 to 2
I have an app that uses sqlite to store some data. I want to add some data to that but don't want to add columns to table or change the table structure at all or add tables to db, I want just add some records to one table.
Is this a point to use OnUpgrade method or it didn't need to use this method for this record adding?
Is this adding some records to table become changing schema?
No, in any structured database adding records doesn't modify the db schema. You can add records like in a SQL databse, using INSERT statements. You can follow the d.android.com tutorial to storing data locally.