-
-
Save LOG-TAG/560755aeb68916a90b6944eccd2eb2b7 to your computer and use it in GitHub Desktop.
Some extension functions with room: Requires Export schema Read this section https://medium.com/google-developers/testing-room-migrations-be93cdb0d975#6872
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.squareup.moshi.Json | |
data class DbSchema(@field:Json(name = "formatVersion") val formatVersion: Int? = 0, @field:Json( | |
name = "database") val database: Database? = Database()) | |
data class Database(@field:Json(name = "version") val version: Int = 0, @field:Json(name = "identityHash") val identityHash: String? = "", @field:Json( | |
name = "entities") val entities: List<Entity?>? = listOf(), @field:Json(name = "setupQueries") val setupQueries: List<String?>? = listOf()) | |
data class Entity(@field:Json(name = "tableName") val tableName: String? = "", @field:Json(name = "createSql") val createSql: String? = "", @field:Json( | |
name = "fields") val fields: List<Field?>? = listOf(), @field:Json(name = "primaryKey") val primaryKey: PrimaryKey? = PrimaryKey(), @field:Json( | |
name = "indices") val indices: List<Indice?>? = listOf(), @field:Json(name = "foreignKeys") val foreignKeys: List<ForeignKey?>? = listOf()) | |
data class Indice(@field:Json(name = "name") val name: String? = "", @field:Json(name = "unique") val unique: Boolean? = false, @field:Json( | |
name = "columnNames") val columnNames: List<String?>? = listOf(), @field:Json(name = "createSql") val createSql: String? = "") | |
data class PrimaryKey(@field:Json(name = "columnNames") val columnNames: List<String?>? = listOf(), @field:Json( | |
name = "autoGenerate") val autoGenerate: Boolean? = false) | |
data class Field(@field:Json(name = "fieldPath") val fieldPath: String? = "", @field:Json(name = "columnName") val columnName: String? = "", @field:Json( | |
name = "affinity") val affinity: String? = "", @field:Json(name = "notNull") val notNull: Boolean? = false) | |
data class ForeignKey(@Json(name = "table") val table: String? = "", @Json(name = "onDelete") val onDelete: String? = "", @Json( | |
name = "onUpdate") val onUpdate: String? = "", @Json(name = "columns") val columns: List<String?>? = listOf(), @Json( | |
name = "referencedColumns") val referencedColumns: List<String?>? = listOf()) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Callback DSLs | |
Room.databaseBuilder(application, | |
AppDatabase::class.java, | |
"Demo.db").onCreate { db: SupportSQLiteDatabase -> | |
}.onOpen { db: SupportSQLiteDatabase -> }.addMigration(oldVersion = 1, newVersion = 2) {db:SupportSQLiteDatabase-> | |
val migrationUtils = MigrationUtils(db, | |
context = application.applicationContext, | |
moshi = moshi) | |
val migrationStatements = migrationUtils.getMigrationStatements("com.zoomi.globalretailer.db.AppDatabase/2.json") | |
migrationStatements.forEach { | |
db.execSQL(it) | |
} | |
}.build() | |
//Dagger example from Production App | |
@Provides | |
@AppScope | |
fun providesAppDB(application: GlobalRetailerApp, moshi: Moshi) = Room.databaseBuilder( | |
application, | |
AppDatabase::class.java, | |
"Demo.db").addMigrationList(MigrationUtils(application.applicationContext, | |
moshi).getMigrations()).build() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//To make this class work, schema must be exported. | |
// To export the schema, follow Prerequisites section from below link | |
//https://medium.com/google-developers/testing-room-migrations-be93cdb0d975#6872 | |
class MigrationUtils(val context: Context, val moshi: Moshi) { | |
private fun getSchemas(): MutableList<DbSchema> { | |
Timber.tag("Schemas") | |
val name = AppDatabase::class.java.name | |
Timber.d(name) | |
val path = [email protected](name) | |
Timber.tag("Schemas") | |
Timber.d(path.joinToString()) | |
val tableInfoArray = mutableListOf<DbSchema>() | |
path.map { getTableInfoFromJson("$name/$it") }.forEach { | |
it?.let { | |
tableInfoArray += it | |
} | |
} | |
return tableInfoArray | |
} | |
private fun getTableJson(fileName: String): String = [email protected]( | |
fileName = fileName) | |
private fun getTableInfoFromJson(fileName: String): DbSchema? { | |
val jsonString = getTableJson(fileName) | |
return jsonString.fromJson<DbSchema>(moshi) | |
} | |
fun getMigrations(): List<Migration> { | |
val migrations = mutableListOf<Migration>() | |
val dbSchemas = getSchemas() | |
dbSchemas.reduce { acc, dbSchema -> | |
if (acc.database != null && dbSchema.database != null) { | |
migrations += getMigrationStatement(acc.database, dbSchema.database) | |
} | |
dbSchema | |
} | |
return migrations | |
} | |
private fun getMigrationStatement(first: Database, second: Database): Migration { | |
val statementsToExecute = mutableListOf<String>() | |
val firstEntities = first.entities ?: listOf() | |
val secondEntities = second.entities ?: listOf() | |
for (entity in secondEntities) { | |
entity?.let { (tableName, _, fields, _, indices) -> | |
val firstEntity = firstEntities.first { tableName == it?.tableName } | |
//Compare Columns | |
val firstColumns = firstEntity?.fields ?: listOf() | |
val secondColumns = fields ?: listOf() | |
val newColumns = secondColumns.filterNot { firstColumns.contains(it) } | |
//Add new Columns | |
for (column in newColumns) { | |
column?.let { | |
val constraint = if (it.notNull == true) "NOT NULL DEFAULT ${getDefaultValue( | |
it.affinity)}" else "" | |
val migrationStatement = "Alter table '$tableName' Add column '${it.columnName}' ${it.affinity} $constraint" | |
Timber.d(migrationStatement) | |
statementsToExecute += migrationStatement | |
} | |
} | |
//Indices | |
val firstIndices = firstEntity?.indices ?: listOf() | |
val secondIndices = indices ?: listOf() | |
val newIndices = secondIndices.filterNot { firstIndices.contains(it) } | |
for (newIndex in newIndices) { | |
newIndex?.let { | |
val migrationStatementDrop = "Drop Index ${it.name}" | |
val migrationStatementReCreate = it.createSql ?: "" | |
Timber.d(migrationStatementDrop) | |
Timber.d(migrationStatementReCreate) | |
statementsToExecute += migrationStatementDrop | |
statementsToExecute += migrationStatementReCreate | |
} | |
} | |
} | |
} | |
return object : Migration(first.version, second.version) { | |
override fun migrate(database: SupportSQLiteDatabase) { | |
try { | |
for (statement in statementsToExecute) { | |
Timber.tag("Statement") | |
Timber.w(statement) | |
database.execSQL(statement) | |
} | |
} catch (e: Exception) { | |
e.printStackTrace() | |
} | |
} | |
} | |
} | |
private fun getDefaultValue(affinity: String?): String = when (affinity ?: "") { | |
"NUMBER" -> "0" | |
"TEXT" -> "''" | |
else -> "''" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
inline fun <T : RoomDatabase?> RoomDatabase.Builder<T>.callback(crossinline onCreateFun: (SupportSQLiteDatabase) -> Unit = {}, | |
crossinline onOpenFun: (SupportSQLiteDatabase) -> Unit = {}): RoomDatabase.Builder<T> { | |
addCallback(object : RoomDatabase.Callback() { | |
override fun onCreate(db: SupportSQLiteDatabase) { | |
onCreateFun(db) | |
super.onCreate(db) | |
} | |
override fun onOpen(db: SupportSQLiteDatabase) { | |
onOpenFun(db) | |
super.onOpen(db) | |
} | |
}) | |
return this | |
} | |
inline fun <T : RoomDatabase?> RoomDatabase.Builder<T>.onCreate(crossinline onCreateFun: (SupportSQLiteDatabase) -> Unit = {}): RoomDatabase.Builder<T> = callback( | |
onCreateFun = onCreateFun) | |
inline fun <T : RoomDatabase?> RoomDatabase.Builder<T>.onOpen(crossinline onOpenFun: (SupportSQLiteDatabase) -> Unit = {}): RoomDatabase.Builder<T> = callback( | |
onOpenFun = onOpenFun) | |
inline fun <T : RoomDatabase> RoomDatabase.Builder<T>.addMigration(oldVersion: Int, | |
newVersion: Int, | |
crossinline migrateFun: (SupportSQLiteDatabase) -> Unit = {}): RoomDatabase.Builder<T> { | |
return addMigrations(object : Migration(oldVersion, newVersion) { | |
override fun migrate(database: SupportSQLiteDatabase) { | |
migrateFun(database) | |
} | |
}) | |
} | |
fun <T : RoomDatabase> RoomDatabase.Builder<T>.addMigrationList(migrations: List<Migration>): RoomDatabase.Builder<T> = addMigrations(*migrations.toTypedArray()) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment