Migrate existing Room database from :app to :databaseModule - android

Working on an app that suffered the downfall of becoming a monolith, thus crippling code maintenance.
Currently the focus has been on improving multiple aspects of the app code, and one of them is to make the app modular (combination of feature and layers).
This process is complete, but there is a need to migrate any existing database data from previous versions when the user installs this new one and I am unable to find a guide/documentation for this.
How can I migrate an existing Room database data from one module to another module so when users install this new version over the older one they do not lose existing information?

Databases are by default stored in a single place (data/data/<the_package_name>/databases/<the_database_filename>) so the module is irrelevant.
So it's just a matter of doing the migration. In short I'd suggest
Create the modified #Entity annotated classes.
Compile the project.
Look in Android Studio's Android View for java(generated) and find the class that is the same name as the #Database class but suffixed with _Impl. In that class there will be a method named createAllTables. It contains all of the SQL to create the tables and other database components, such as Indexes, Triggers and Views.
Create the core Migration code.
for each table, use the SQL but modified with changed table names, to create new tables.
for each table copy the data, if suitable using INSERT INTO the_new_table SELECT * FROM the_original_table
see and read https://www.sqlite.org/lang_insert.html
if you have Foriegn Key constraints then parent tables MUST be created before the child tables throughout
if you have new columns with the NOT NULL constraint then you will have to cater for this somehow.
After the data has all been copied you can rename the original tables user ALTER TABLE RENAME TO another_name
Rename the new tables so they have the correct name.
DROP the renamed (another_name) tables.
note children will have to be dropped before parents.
You could move 10 to replace 8 (but it is safer to rename and then delete)

Related

Room - How to explicitly create all tables first time via migration?

I want to explicitly create all DB tables in an empty database myself via migration.
How can I do this?
Room always creates initial tables itself using entity classes. This is an unreliable approach. I can't control schema and have to rely on Room.
Room always creates initial tables itself using entity classes. This is an unreliable approach.
It is not unreliable in fact the opposite. However, what should be understood is that Room is about storing and retrieving objects when the underlying database manager, SQLite, has no concept of objects, just tables with columns. As such Room has to be able to create the objects (Entities) from the columns in a row or rows and hence rules that must be met. If met then Room is quite reliable.
I can't control schema and have to rely on Room.
You can and do by the way of coding the entities. However, the rules come with limitations or perhaps more correctly proper/formal use of SQLite. So you have control over the schema but it must adhere to Room's rules/limitations.
An example is column types.
SQLite has a very flexible approach to column types. In that virtually anything can be a accepted as a column type.
Room however, must know how to store/extract the object (entity). As such Room restricts column types to INTEGER, TEXT, REAL or BLOB (the 4 types that SQLite supports). Room does not support the catch-all NUMERIC type and it does not support SQLite's conversion of other column types to a type via SQLite's rule for determining type affinity.
Room is NEVER going to accept a column defined as timestamp DATE as an example as DATE is not one of INTEGER, TEXT, REAL or BLOB (in SQLite DATE resolves to a type affinity of NUMERIC, the catch all).
I want to explicitly create all DB tables in an empty database myself via migration. How can I do this?
In short do one of the following:
Follow Room's rules when defining the schema,
Be smart and let Room do the work (as explained below), or
Use native SQLite rather than Room.
The smart way, as you must have Entities, is to create the Entities and the class annotated with #Database and then compile. Doing so will generate code with the expected schema. The generated code is in the generated java, visible via the Android View in Android Studio. It is in the class that is the name of the class annotated with #Database suffixed with _Impl. In the createAllTables method are the create table SQL statements that Room uses if and when Room creates the tables. This is what Room expects and will adhere to the rules.
You can then use the SQL created by Room as the basis for creating or modifying tables.
You can do many things in the Migration. The creation of new tables, if new tables are introduced, would be a requirement as the Migration is passed an unchanged database. It is the job of the Migration to alter the database and thus create new tables if that is required according to the Entities defined in the #Database annotation.

android room the reinstall app migration is not running

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

Adding additional columns to SQLite from previous Android app version

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.

Generating a SQLite database dynamically

I am building an application which is a form generator (it creates a form with a SQLite database based on a configuration file). The problem is that the database will never be the same, so I need to make it dynamic meaning that I want to be able to specify all the table rows and tables of the database.
The problem I have is that, since it's a configuration file, when I create the database I dont know yet what are the tables and/or the table rows so I am not able to rely on the onCreate() of the database.
I was wondering if there would be a better way to proceed other than overriding the onCreate() to do nothing and making my own tableCreate() function.
I don't know if this is clear enough since english is not my native language but I will edit my question if I need to. And by the way I am new to android so snipet + explications are appreciated.
When the application loads, it creates the database using the configuration file (a simple text file) that is pushed to the application (only if the database does not exist).
Then it creates the tables based on the configuration file again (the name of the table rows and type of data).
The application builds the form based on the configuration file with an attribute which will allow me to save the answer in the database created previously.
This application's goal is to be able to create new forms efficiently and in a really quick way in order to gather some informations on given person.
Ok so I managed to build a workaround:
what I came up with is that I have a db with 4 fields (_id, farmerId, fieldName and fieldValue) this allows me to have some kind of value key for a specific farmer.
Then I builded some functions that generate a JSONObject with the different rows concerning a specific farmer and returns it to my activity. This way, it does not really matters if an "object" would need 5 or 6 fields.

ActiveAndroid: Upgrade Database version

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;

Categories

Resources