Last active
May 31, 2020 15:25
-
-
Save t3rmian/8ffe844882d4c009abd730cb98f75dac to your computer and use it in GitHub Desktop.
Android Room - Backup to a secondary database on a destructive migration
This file contains 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 android.app.Application | |
import androidx.room.Room | |
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory | |
import dagger.Module | |
import dagger.Provides | |
import javax.inject.Singleton | |
@Module( | |
subcomponents = [ | |
ActivityComponent::class, | |
FragmentComponent::class | |
] | |
) | |
class AppModule(private val app: Application) { | |
private val migrationDatabase: Lazy<MigrationDatabase> = lazy { | |
Room.databaseBuilder(app.applicationContext, MigrationDatabase::class.java, "migration.db") | |
.fallbackToDestructiveMigration() | |
.build() | |
} | |
@Provides | |
@AppScope | |
fun provideApp(): Application = app | |
@Provides | |
@Singleton | |
fun provideDatabase(productApi: ProductApi): ProductDatabase { | |
return Room | |
.databaseBuilder(app.applicationContext, ProductDatabase::class.java, "prod.db") | |
.fallbackToDestructiveMigration() | |
.openHelperFactory( | |
BackupOpenHelperFactory( | |
FrameworkSQLiteOpenHelperFactory(), | |
listOf(ProductMigrationBackup(migrationDatabase)) | |
) | |
) | |
.build() | |
} | |
} |
This file contains 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 androidx.sqlite.db.SupportSQLiteDatabase | |
interface Backup { | |
fun backup(database: SupportSQLiteDatabase) | |
} |
This file contains 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 androidx.sqlite.db.SupportSQLiteDatabase | |
import androidx.sqlite.db.SupportSQLiteOpenHelper | |
import timber.log.Timber | |
class BackupCallback( | |
private val delegate: SupportSQLiteOpenHelper.Callback, | |
private val backups: List<Backup> | |
) : SupportSQLiteOpenHelper.Callback(delegate.version) { | |
override fun onDowngrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) { | |
backups.forEach { it.backup(db) } | |
delegate.onDowngrade(db, oldVersion, newVersion) | |
} | |
override fun onCreate(db: SupportSQLiteDatabase) { | |
delegate.onCreate(db) | |
} | |
override fun onOpen(db: SupportSQLiteDatabase) { | |
delegate.onOpen(db) | |
} | |
override fun onConfigure(db: SupportSQLiteDatabase) { | |
delegate.onConfigure(db) | |
} | |
override fun onCorruption(db: SupportSQLiteDatabase) { | |
try { | |
backups.forEach { it.backup(db) } | |
} catch (e: Exception) { | |
Timber.e(e, "Could not backup the corrupted database") | |
} | |
delegate.onCorruption(db) | |
} | |
override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) { | |
backups.forEach { it.backup(db) } | |
delegate.onUpgrade(db, oldVersion, newVersion) | |
} | |
} |
This file contains 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
class BackupOpenHelperFactory( | |
private val delegate: SupportSQLiteOpenHelper.Factory, | |
private val backups: List<Backup> | |
) : | |
SupportSQLiteOpenHelper.Factory { | |
override fun create(configuration: SupportSQLiteOpenHelper.Configuration): SupportSQLiteOpenHelper { | |
val decoratedConfiguration = | |
SupportSQLiteOpenHelper.Configuration.builder(configuration.context) | |
.name(configuration.name) | |
.callback(BackupCallback(configuration.callback, backups)) | |
.build() | |
return delegate.create(decoratedConfiguration) | |
} | |
} |
This file contains 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 androidx.room.TypeConverter | |
import com.google.gson.Gson | |
import com.google.gson.reflect.TypeToken | |
object MapConverter { | |
private val GSON = Gson() | |
@TypeConverter | |
@JvmStatic | |
fun stringToMap(value: String): Map<String, String> { | |
return GSON.fromJson(value, object : TypeToken<Map<String, String>>() {}.type) | |
} | |
@TypeConverter | |
@JvmStatic | |
fun mapToString(value: Map<String, String>): String { | |
return GSON.toJson(value) | |
} | |
} |
This file contains 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 androidx.room.* | |
@Dao | |
interface MigrationDao { | |
@Query("SELECT * FROM migration_data") | |
fun findAll(): List<MigrationData> | |
@Insert(onConflict = OnConflictStrategy.IGNORE) | |
fun insert(data: MigrationData) | |
@Delete | |
fun delete(data: MigrationData) | |
} |
This file contains 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 androidx.room.* | |
@Entity(tableName = "migration_data") | |
data class MigrationData( | |
@ColumnInfo(name = "data") val data: Map<String, String>, | |
@PrimaryKey @ColumnInfo(name = "id") var id: Int = data.hashCode() | |
) |
This file contains 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 androidx.room.* | |
@TypeConverters(value = [MapConverter::class]) | |
@Database(entities = [MigrationData::class], version = 2) | |
abstract class MigrationDatabase : RoomDatabase() { | |
abstract fun migrationDao(): MigrationDao | |
} |
This file contains 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 androidx.room.* | |
import java.util.* | |
@Entity( | |
tableName = "products", indices = [Index( | |
value = ["name", "tag"], | |
unique = true | |
)] | |
) | |
data class Product( | |
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Int = 0, | |
@ColumnInfo(name = "code") val code: String, | |
@ColumnInfo(name = "name") val name: String, | |
@ColumnInfo(name = "creation_date") val creationDate: Date, | |
@ColumnInfo(name = "tag") val tag: String? = null, | |
@ColumnInfo(name = "image") val image: ByteArray? = null | |
) |
This file contains 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 androidx.room.* | |
@Dao | |
interface ProductDao { | |
@Query("SELECT * FROM products") | |
fun findAll(): List<Product> | |
@Insert | |
fun insert(product: Product) | |
@Delete | |
fun delete(user: Product) | |
} |
This file contains 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 androidx.room.* | |
@TypeConverters(value = [MapConverter::class]) | |
@Database(entities = [Product::class], version = 25) | |
abstract class ProductDatabase : RoomDatabase() { | |
abstract fun productDao(): ProductDao | |
} |
This file contains 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 androidx.sqlite.db.SupportSQLiteDatabase | |
import dev.termian.nutrinote.data.db.MigrationDatabase | |
import dev.termian.nutrinote.data.dto.MigrationData | |
class ProductMigrationBackup(private val migrationDatabase: Lazy<MigrationDatabase>) : Backup { | |
override fun backup(database: SupportSQLiteDatabase) { | |
val migrationDao = migrationDatabase.value.migrationDao() | |
database.query("SELECT code FROM products").use { cursor -> | |
while (cursor.moveToNext()) { | |
migrationDao.insert( | |
MigrationData( | |
data = mapOf( | |
"table_name" to "products", | |
"code" to cursor.getString(0) | |
) | |
) | |
) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment