I'm using Room in Android for my databases. Recently I had to create an alternate for my main database so now I have 2 databases.
abstract class FirstDatabase : RoomDatabase()
abstract class ScondDatabase : RoomDatabase()
I have a table in the FirstDataBase that I want to copy to the SecondDataBase. I know I should write migration but I don't know how I should do that.
This is my current migration with dagger for room:
fun provideDatabaseMigration56(): Migration {
return object : Migration(DATABASE_VERSION_5, DATABASE_VERSION_6) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS `pins`")
}
}
}
fun provideDatabaseMigration45(): Migration {
return object : Migration(DATABASE_VERSION_4, DATABASE_VERSION_5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `pins` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `slug` TEXT NOT NULL)")
}
}
}
What you're looking for is the SQLite ATTACH DATABASE statement. This allows you to attach an additional database to the current connection and then run queries referencing tables within both. There is some detailed documentation here which shows the syntax:
ATTACH DATABASE file_name AS database_name;
You can then run queries referencing tables from both databases, but make sure that when referencing a table from the attached database that you refer to it as
database_name.table_name
as opposed to just table_name.
Related
private fun savetosqlite(CoinListesi: List<CoinInfo>){
launch{
val dao = CoinInfoDatabase(getApplication()).CoinDao()
dao.deleteAll()
val uuidList= dao.insertAll(*CoinListesi.toTypedArray())
}
dao is reset but primary key keeps increasing every time the function is called, also primary key doesn't start from 0 how do I solve it?
Dao
#Dao
interface CoinInfoDao {
#Insert
suspend fun insertAll(vararg CoinInfo: CoinInfo):List<Long>
#Query("DELETE FROM CoinInfo")
suspend fun deleteAll() }
model
#Entity
data class CoinInfo (...){
#PrimaryKey(autoGenerate = true)
var uuid:Int=0
}
Because autoGenerate/autoIncreament from the sqlite docs says it is to
prevent the reuse of ROWIDs over the lifetime of the database
As deleting all the rows with "DELETE FROM CoinInfo" does not affect the lifetime of this table in the database then the numbers continue to increase.
You need to end the "life" of the table with the SQL "DROP TABLE CoinInfo" and then re-create it for it to start a new lifetime and reset the auto generated Int.
Or you can directly reset the value of where SQLite stores then last number used with a query like "DELETE FROM sqlite_sequence WHERE name='CoinInfo'" (or set the value to be 0/1 of this row)
You would need to execute something like
CoinInfoDatabase.getDatabase(application)
.getOpenHelper()
.getWritableDatabase()
.execSQL("DELETE FROM sqlite_sequence WHERE name='CoinInfo'");
Or more efficient is do you really need autoincrement? as per the docs
Problem - Room DB getting wiped/cleared when doing force update play store update. I am working on a chat messenger application which uses Room DB as local database. Whenever I do a store update with increasing DB version, the local DB gets cleared and messages history are lost.
I'm Using Room DB. My Application is in the Play Store with the use of Room DB and the version is 4.
My Question is I'm changing the 9 tables schema, and now that I update the DB version, each table schema changes. Should I increase the DB version here? How can I accomplish this without losing the user data using Room DB for force update in Play Store? Ex. DB version is 4, I change the two tables’ elements like in the below query.
Do I need to increase DB version twice as two tables are changed or change to one number incremental will be fine? Example: Do I need DB to increase version to 6 OR keeping it 5 is enough?
private val mMigrationMessageStatus: Migration = object : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE message_status RENAME TO MessageStatus")
database.execSQL("ALTER TABLE MessageStatus ADD COLUMN userId TEXT NOT NULL default ''")
}
}
private val mMigrationGroupMember: Migration = object : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE group_member RENAME TO GroupMember")
database.execSQL("ALTER TABLE GroupMember ADD COLUMN userId TEXT NOT NULL default ''")
}
}
return Room.databaseBuilder(context, AppDatabase::class.java, dbName)
.allowMainThreadQueries()
.addMigrations(mMigrationMessageStatus,mMigrationGroupMember)
.build()
From room version 2.4.0, you can easily update using autoMigrations.
DATABASE CLASS
#Database(
version = 3,
autoMigrations = [
AutoMigration(from = 1, to = 2),
AutoMigration(from = 2, to = 3)
],
.....
)
DATA CLASS
#Entity(tableName = "user")
data class DataUser(
....
// I added this column, like this
#ColumnInfo(defaultValue = "")var test: String = ""
)
see reference below
android developer: room version2.4.0
android developer: autoMigration
OWASP tool gives warning of sql injection while writing raw sql query in Room migration. Is there any solution to overcome for this problem. Below is my code:
class Migration1to2 : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER")
}
}
I'm using Room and I need to perform a database migration. I've migrated the data but I have a problem in one of the columns. When the migration is performed, the data for that column may still be unavailable.
When the user enters the data needed for that column, I have to get all rows that match a value in that column, update these values by the one provided by the user and drop all other rows that do not match.
I can have a method in my UserDao but the problem is that this does not seem correct because it's a one time only thing and I don't what to expose the method so my idea was to get the database instance and try to do the change myself.
When I use
var myDatabase = Room.databaseBuilder(....)
.addMigrations(... .build()
I keep a reference to it but then, when I do myDatabase.openHelper.writableDatabase I'm always getting an exception
getDatabase called recursively
Any idea how to handle this?
Your issue is that you are trying to use the MyDatabase's openHelper to try to get the database when building the instance of MyDatabase which is in the process of getting the database, so while getting the database you are then trying to get the database.
Instead you need to use the SupportSQLiteDatabase that is passed to the Migration.
As ean example :-
#Database(
version = 1,
entities = [
MyTableEntity::class
]
)
abstract class AppDatabase : RoomDatabase() {
abstract fun MyTableEntityDao(): MyTableEntityDao
companion object {
val MIGRATION_V1_V2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
//........ code using the already opened database
database.execSQL(????????); //<<<<<<<<<< USES the support database
}
}
}
}
This would then be invoked using something similar to :-
var myDatabase = Room.databaseBuilder(applicationContext,AppDatabase::class.java,"mydatabase")
.allowMainThreadQueries()
.addMigrations(AppDatabase.MIGRATION_V1_V2)
.build()
I'm saving
data class Settings(
val foo: Int
)
into my room database version 1.
Now I need to extend Settings to
data class Settings(
val foo: Int,
val bar: ArrayList<Baz>
)
where
enum class Baz {
A, B, C
}
so I need to do a migration to version 2.
I have type converters for bar. I'm currently trying something like
val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE settings ADD COLUMN bar TEXT")
}
}
but this gives me an IllegalStateException: Migration didn't properly handle Settings... error.
And I'm stuck. So please help! How do I get that migration working??
Turns out it was my lack of SQLite skills that was in the way.
First of all, I needed to set a default value since my barwasn't allowed to be null. Second, ALTER TABLE is crazy limited and didn't allow me to set a default value in one line.
I ended up doing
val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE settings_new (foo INTEGER NOT NULL, bar TEXT NOT NULL, PRIMARY KEY(foo))")
database.execSQL("INSERT INTO settings_new (foo, bar) SELECT foo, '[]' AS bar FROM settings")
database.execSQL("DROP TABLE settings")
database.execSQL("ALTER TABLE settings_new RENAME TO settings")
}
}
In other words
Create a new temporary table (settings_new)
Move old values into the new one. Notice that we set bar as an empty array as default.
Drop the original (settings) table
Rename the temporary to the old name (i.e. settings_new --> settings)
Try to change settings table name Settings and you pass only setting..
database.execSQL("ALTER TABLE Settings ADD COLUMN bar TEXT")
after add..
database = Room.databaseBuilder(context.getApplicationContext(),
UsersDatabase.class, "Sample.db")
.addMigrations(MIGRATION_1_2)
.build();
refer this ..
Room database migration if only new table is added