Migrate embedded object to separate table. Room - android

CertificateElementEntity entity had an embedded class ImgData.
I have divided CertificateElementEntityand ImageData into separate tables.
But now I can't figure out how to make the migration.
open class CertificateElementEntity(
#IgnoreJson
#PrimaryKey
#ColumnInfo(name = "local_id")
var localId: Long? = null,
var data: String? = null,
var imageData: ImgData? = null)
Maybe someone made similar migrations

You can try this general migration schema (honestly, I haven't experienced such a migration, so may be there is way easier):
Create temporary table [CertificateElementEntityTemp] with the same structure.
Copy all data from table [CertificateElementEntity] to [CertificateElementEntityTemp].
Drop table [CertificateElementEntity].
Create table [ImgData].
Create table [CertificateElementEntity] with new structure (with just imageId instead all fields from embedded table). Create Foreign Key for [imageId].
Copy needed data from [CertificateElementEntityTemp] to [ImgData].
Copy needed data from [CertificateElementEntityTemp] to [CertificateElementEntity].
Drop table [CertificateElementEntityTemp].
All this of course you should write in migration section with equivalent SQL statements.

Related

Storing image data in Room database with or without using (typeAffinity = ColumnInfo.BLOB)

I know it's not the best practice to store an image in DB directly and we should store a path instead. But in my case this is a must.
I am able to store list of images perfectly fine defined as:
#ColumnInfo(name = "picture")
var picture: ByteArray? = null
I came across solutions that suggests using (typeAffinity = ColumnInfo.BLOB). So I changed my column to:
#ColumnInfo(name = "picture", typeAffinity = ColumnInfo.BLOB)
var picture: ByteArray? = null
I haven't noticed anything significant in performance. I wonder what are the possible advantages of using typeAffinity or disadvantages of not using it?
It maybe worth mentioning my images are always under 1 megabytes.
There is no real advantage/disadvantage, certainly not at run time perhaps marginally at compile time.
That is all that using typeAffinity=? does is override the typeAffinity being determined by the type of the field/column of the variable.
As you have var picture: ByteArray this would be resolved to a column type of BLOB anyway.
If you wished you could compile with both and see the resultant SQL used by looking at the generated java.
Perhaps consider the following Entity that uses both:-
#Entity(tableName = "user")
data class UserEntity(
#PrimaryKey
val userid: Long = 0,
var picture1: ByteArray? = null,
#ColumnInfo(typeAffinity = ColumnInfo.BLOB)
var pitcure2: ByteArray? = null
)
In the generated Java (use Android view as highlihted) then in the #Database class (UserDatabase in the example) suffixed by _Impl (so UserDatabase_Impl in the example) the following is a screen shot of the generated Java :-
Android highlighted indicates where to select the Android view.
The highlight in the code explorer shows the respective code (UserDatabase_Impl) in the expanded java (generated) directory
The createAllTables method is the method used to create the table(s)
room_master_table is a room specific table used for verification of an existing table with the schema to detect if there are differences.
The code (SQL) generated for the creation of the table is :-
_db.execSQL("CREATE TABLE IF NOT EXISTS `user` (`userid` INTEGER NOT NULL, `picture1` BLOB, `pitcure2` BLOB, PRIMARY KEY(`userid`))");
i.e. the definition of the columns picture1 and picture2 are identical bar the column names.
NOTE please heed the WARNING in regards to not changing the generated code.

how to write migration for newly added table in room database android

Can someone help me here, As i wanted to alter existing room table(User) by adding new column which is object(address).
`
data class UserDetailsEntity (
#PrimaryKey
var id: Int = 0,
#ColumnInfo(name ="name")
val name:String? = null,
#ColumnInfo(name = "lastName")
val lastName: String ?= null,
#Embedded
val address:Address ?= null,
)
data class Address (
#ColumnInfo(name ="lat")
val lat : String? = null,
#ColumnInfo(name = "long")
val long: String ?= null,
)
`
How to write migration for above scenario? Thanks in advance.
Do I need to write migration for this? (from comment)
Yes as the table is being altered.
By Embedding the Address then room will consider that a UserDetailsEntity object will have an Address object within it. Room will therefore build the code to set the objects values.
Additionally room will expect the table UserDetailsEntity (or whatever value is associated with the table) to have two extra columns lat and long. Therefore the migration needs to add the 2 columns. You would use 2 ALTER TABLE <tablename> ADD COLUMN <column_definition>; in the migration.
Note if you compile (Ctrl + F9) with the changes made then look in the Java(generated) subfolders, whilst in Android view of the Project window, and then look at the class, followed by _impl where you have the #Database in the createAllTables method. The SQL for the creation of the table will exist. You can then copy and paste the column definitions.
Example screen shot where the class Database is where the #Database is coded and thus Database_impl is the generated java to look at that has the create table SQL for the tables :-

How to insert a duplicate row in Room Db?

While searching for this, I only came across people asking how to Avoid inserting duplicate rows using room db. But my app has a feature where the user may tap a copy button and the list item will get inserted again in the db. I could have simply achieved this if my table didn't have a primary key set on one of its fields. While I found this solution for SQLite, I don't know how I can achieve this in Room Db. Because while writing an insert query with custom queries in room would defeat the purpose of using room in the first place.
Let's say you have some entity
#Entity(tableName = "foo_table")
data class Foo (
#PrimaryKey(autoGenerate = true) var id: Int,
// or without autogeneration
// #PrimaryKey var id: Int = 0,
var bar:String
)
and you have some Dao with insert:
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(foo: Foo)
Then to copy your existing value (copiedValue: Foo) you need in some way to manage your primary key:
Scenario 1. Your Primary Key is autogenerated, you have to set it to default value to get new autogenerated one:
copiedValue.id = 0
yourDao.insert(copiedValue)
Scenario 2. Your Primary Key is not autogenerated, you have to set new primary key manually:
copiedValue.id = ... // some code to set new unique id
yourDao.insert(copiedValue)

Android - Migrating to Room Database

I need to migrate from Sqlite database to room in my application, but before migrating i have some doubts regrading room database i have searched through many sites but i have not satisfied with the solution.
My table schema will be changing frequently through API calls, is it possible in room ?
We need entities to map Column Name with entity properties, Since my table schema changes frequently how do i create the entity ?
Is it possible to map the multiple columns to single field property in entity, like say
Table A has three columns COL_A,COL_B,COL_C
#Entity
class TableA{
#ColumnInfo("COL_A")
val columnA : String
#ColumnInfo("COL_B,COL_C")
val columns : Map<String,String>
}
Is it possible to create entity like above in room?
Can anyone clear the above doubts ?

Android Room: How to Migrate Column Renaming?

Issue
My app is crashing because I am not handling migration properly. I'm looking for a solution to migrate the name of 1 column in my table.
In my project I a have a room table named 'content' with a Double attribute 'archivedCount'. In the latest version of the app the attribute archivedCount attribute is re-named to dismissCount, still as type Double.
Original Content model
#Entity(tableName = "content")
data class Content(#PrimaryKey var id: String, var archiveCount: Double) : Parcelable {...}
New Content model
#Entity(tableName = "content")
data class Content(#PrimaryKey var id: String, var dismissCount: Double) : Parcelable {...}
Attempted Solution
After reading a Google Developer Advocate's explanation Understanding migrations with Room, I attempted her solution outlined in the post's section Migrations with complex schema changes which entails making a copy of the original table, deleting the old table, then renaming the newly created table.
With the following approach below there is a runtime error on this line: database.execSQL("INSERT INTO content_new (id, dismissCount) SELECT id, archiveCount FROM users"); because I already cleared my app's cache so the old table no longer exists.
Can I update a single column without re-creating the entire table?
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
// Create the new table
database.execSQL(
"CREATE TABLE content_new (id TEXT, dismissCount REAL, PRIMARY KEY(id))");
// Copy the data
database.execSQL("INSERT INTO content_new (id, dismissCount) SELECT id, archiveCount FROM users");
// Remove the old table
database.execSQL("DROP TABLE content");
// Change the table name to the correct one
database.execSQL("ALTER TABLE content_new RENAME TO content");
}
};
Solution
Thanks to the guidance from #TimBiegeleisen we discovered that the Android implementation of SQLite 3.19 for API 27 and 28 has not yet upgraded to the version 3.25 SQLite which allows this feature outlined in this StackOverflow post.
Once Android upgrades a command such as this to alter a table column will be possible: database.execSQL("ALTER TABLE content RENAME COLUMN archiveCount TO dismissCount")
There is a solution without migration - use ColumnInfo:
data class Content(#PrimaryKey var id: String, #ColumnInfo(name = "archiveCount") var dismissCount: Double) : Parcelable{...}
Database column will be still archiveCount, but in Kotlin property will be renamed.

Categories

Resources