Room DB Upgrade need to write full insert query - android

Hi I am developing an android app and I am using room DB and I have created one table and now I want to add another table I need to write a migration for that and in-migration I need to write full SQL queries for the new table assume I have more than 20 fields how complex will be the query.
In SQLite, we need to write such complex queries which sometimes becomes complicated to write and find errors So, room DB came to resue but now we need to do the same in the room DB migration. How does it useful then?
If we use SQLite, then if we already added one table and now We want to add another table then we can just uninstall the application and new tables will be generated, but in-room DB this is not a case I tried the same thing but it is still showing me that you need to write a migration for the new table. So, in this case, while developing there will a lot of migration scripts that will be hard to maintain at some point in the future.
How does it useful then I have to write a multiple create queries while developing the app this is a very basic flow in any application.
Once we go to prodution then it makes sense to write migration for every table but not in the developing mode.
How does room DB make developr job eaiser?

I have more than 20 fields how complex will be the query.
It can be very simple as an Entity defines an Object e.g. your 20 columns and to get the 20 columns can be as simple as
#Query(SELECT * FROM thetable)
List<Thetable> getAll();
The above being in an Interface that is annotated with #Dao and all you do in the code is retrieve an instance from the built room database and then use the getAll method which returns a List of Thetable objects. Each with all the member variables populated from the database.
e.g. you could have :-
mMyTheTableDaoObject = mMyBuiltRoomDatabase.getAll();
List<TheTable> myTheTableList = mMyTheTableDaoObject.getAll();
for(TheTable t: myTheTableList) {
int current???? = t.get????();
}
While using standard/non-room then you would have to do something along the lines of :-
SQLitedatabase db = whatever_you_need_to_do_to_get_an_SQLiteDatabase_instance;
Cursor c = db.query("theTable",null,null,null,null,null,null);
ArrayList<TheTable> myTheTableList = new ArrayList();
while(c.moveToNext()) {
currentTheTable = new TheTable();
current.TheTable.setId = c.getLong(c.getColumnIndex("id");
current.TheTable.setNextColumn1 = c.getString("next_column1");
current.TheTable.setNextColumn2 = c.getString("next_column2");
........ another 17 similar lines of code
currentTheTable.setNextColumn20 = c.getString("next_column20");
myTheTableList.add(currentTheTable);
}
for(TheTable t: myTheTableList) {
int current???? = t.get????();
}
If we use SQLite, then if we already added one table and now We want to add another table then we can just uninstall the application and new tables will be generated, but in-room DB this is not a case I tried the same thing but it is still showing me that you need to write a migration for the new table.
Once we go to production then it makes sense to write migration for every table but not in the developing mode.
Rather then migrating simply delete the database (delete the App's data or uninstall the App, the database is stored in the default location (data/data/package_name/databases)) and rerun without changing the version. The database will be created as per the new schema. Perhaps utilising temporary code to load data accordingly.
How does room DB make developr job eaiser?
In Short ROOM generates what is termed as the boilerplate code from relatively simple code e.g the #Query above writes the underlying code to extract the data and build the objects (e.g. the code as above).

Please check the official document:
https://developer.android.com/training/data-storage/room/migrating-db-versions
Actually Room using SQLITE behind the scene. It provide you plethora of other facilities. In case of Migration you have to write full code to create table.

Harsh your question is valid in some way but as you know android is maintaining SQLite database and libraries like room, greendao or even native SQLiteOpenHelper
is handling the transaction with sqllite behind the scene for the developers.
In all the earlier libraries too you have to maintain versions of your database and which fields or tables have been added to your database and write migrations for the version upgrades of the database.
The beauty of room comes in play in how easy they have made the CRUD operations on the SQLite database and getting data wrapped in LiveData or Observable, not that you don't need to write migrations.

Related

Room Migration with prepopulated DB - Is fallbackToDestructiveMigration() my only option?

I have several tables in my DB of which some contain prepopulated content that the user cannot change and others that are only filled by the user. Now I want to update the prepopulated, static content but keep the user generated content.
This Android developer guide says the following concerning my question:
Because there is an implemented migration path from version 2 to version 3, Room runs the defined migrate() method to update the database instance on the device to version 3, preserving the data that is already in the database. Room does not use the prepackaged database file, because Room uses prepackaged database files only in the case of a fallback migration.
So this means I have no possibility to, for example: from 3 columns X, Y & Z - drop columns X and Z and recreate them with new content from the updated DB file, while keeping column Y as it was?
Below is an illustration of the issue.
Is it correct that I only have 2 options now:
Drop tables completely and make user lose their generated content, so they can have the updated prepopulated content
Write migrations for the new structure, but end up with empty columns X.2 and Z.2 because Room will ignore my prepopulated DB 2.0
Take a look at this library, maybe it helps you solve this
https://github.com/ueen/RoomAssetHelper
You can name tables and columns that should be preserved and then rename the existing db, copy the new one and transfer the specified columns.
Example from the GitHub page:
val db = RoomAssetHelper.databaseBuilder(applicationContext,
AppDatabase::class.java,
"chinook.db",
1,
preserve = arrayOf(TablePreserve(table = "yourTable",
preserveColumns = arrayOf("yourColumn"),
matchByColumns = arrayOf("id"))))
.build()

Insertion of rows into sqlite db using room not working during migration

Not sure if sqlite actually supports this, if it's a room db bug, or if I'm doing something wrong, but insertion of rows using predefined data during a migration doesn't actually work:
Using the Persistence Migration Sample as an example:
In the UsersDatabase for the MIGRATION_2_3 I add in this line to the migration:
database.execSQL("INSERT INTO Users (userid, username, last_update) VALUES (456, 'asdf', 123), (999, '3524', 4567);");
In the MigrationTest I add more assertions:
List<User> allUsers = usersDatabase.userDao().getAllUsers();
Assert.assertEquals(4, allUsers.size());
Result:
The test fails and the rows were not created, but the migration was run until completion! I need this working, does anybody have a way to do this or are there any good alternatives?

What is the best way to generate Android Room Migrations?

Note that I come from a .NET background where I'm familiar with Entity Framework.
So in EF migrations, the actual migrations are generated for you. Basically, you specify in your POCO objects the changes you want. When you add a new migration, it does a diff between the database and the POCO objects and generates a migration script. You barely have to even look at it.
So using Android Room, I'm finding I have to handcraft these migrations myself. And there are rules that mean the system crashes unless the migration you handcraft puts the schema in exactly the state Room thinks it should be based on the POJO objects.
For me, this seems like a tedious and risky task that could be automated (like EF migrations already does). So my question is, is there a tool or something that'll do these migrations automatically? If not, what are some guidelines on how to do these migrations safely or efficently?
I'm not sure if a tool like this exists specifically for Room as of today, but I hardly doubt it, since Room's stable version was released just a few months ago.
One small thing you can get generated is SQL statements for tables creation.
Just add:
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
to android/defaultConfig in your's build.gradle and a JSON with complete schemas will be generated.
To do these migrations safely you can fully test them - this is well described here
Regarding efficiency I would recommend creating direct migrations like Migration(1, 4)
The android room migrations are problematic, because even a difference of column order will show a failed migration. But due to sqlite's limitations column type alterations and so forth cannot be done. Do as in Michal's answer and generate the schemas.
Then use a JSON diff tool to see how it generates your particular table.
If the difference is easy, do the necessary alter command in your migration script. I've pretty much given up on doing this and instead just create a new table the way my new version expects it to be, copy the data from the old table, drop it, and rename the correctly created table. Here's some code:
static final Migration MIGRATION_3_4 = new Migration(3, 4) {
#Override
/**
* Instead of trying to read the owl droppings of the error message, simply go to the schemas directory
* and do a table copy of the before and after. Look for the table creation in the updated schema number
* then do an insert by doing a select * from the old
* then remove the old and rename the new
*/
public void migrate(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS crew2 (`crewId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `shortName` TEXT, `isCaptain` TEXT)");
_db.execSQL("INSERT INTO crew2(crewid,name,shortname) SELECT * FROM crew_table");
_db.execSQL("DROP TABLE crew_table");
_db.execSQL("ALTER TABLE crew2 RENAME TO crew_table");
_db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_crew_table_shortName` ON `crew_table` (`shortName`)");
}
};

Use of Direct Queries in SQLite?

I am currently studying SQLite and I have found that it uses various classes like ContentValues for insertion and updation... I was wondering whether I have to follow the given way or can I write a normal SQL query and use db.execSQL() method to execute them?
Will it bring any inconsistency to my database because with these all "extra" steps doesnt it stop the flow of the query and I feel it would be faster if we use a query directly.
You can do any SQL command you want with db.execSQL except select command or any other SQL command that return data (you use db.rawQuery() for this). The classes used are helper classes that make it easy for you to manipulate DBs (try inserting 100 rows with 20 columns each using ContentValues and db.execSQL and you will get the point). For small tables it will not differ much (and you will not cause inconsistecies), however, for large tables with inputs that depend on user interface or use calculations, it might be useful to have a class like ContentValues with its helper methods.
Yes you can definitely use this way like using
myDB.execSQL("INSERT INTO MyTable VALUES ('fffff', 'numb', 20)");
to insert values but only when you are using database for small queries.
Also there are some flaws using direct methods which gets removed using ContentValues
For example,try to insert a blob into the database using this method ,you will get a null bitmap while converting the retrieved data to bitmap.But when you insert using ContentValues,you will get the correct data i.e you will be able to convert that into Bitmap.

Android SQLite Database Update

I have a pre-established SQLite Database in my application. It has a table with rows about 20 rows of text. I want to be able to add additional rows to the table without deleting all of the previous information. The only way I have seen which would allow me to do this is to delete all of the previous databases and then recreate it with the new rows. There must be a better way. Thanks for your help!
Are you confusing rows with columns?
If you really do mean rows then as antlersoft points out, using the SQL INSERT INTO statement will simply add a new row to a table without affecting any existing table data. This is one of the most basic and commonly used SQL statements.
If you actually mean you need to add columns then use the SQL ALTER TABLE statement.
See..
SQL INSERT INTO statement
SQL ALTER TABLE statement
The Android framework, as it relates to SQLite (using a SQLiteOpenHelper) provides two distinct methods for handling database lifecycles - onCreate(), used when the database needs to be created from scratch, and onUpgrade(<database>, int oldVersion, int newVersion) for handling updates. You can specify the "new" version number in the constructor for the superclass of your SQLiteOpenHelper, and the framework knows to call onUpgrade() based on this parameter and the internal version # in the actual sqlite database.
So, to modify your database during a version change just override onUpgrade() and run whatever SQLite stuff that you need.

Categories

Resources