java migration_JetPack知识点实战系列九:Room数据库Migration
在APP的迭代过程中,会遇到给数据库表增加字段或者增加表的的需求,这时候就涉及到数据库的Migration。本节我们将来介绍一下Room的Migration。
上节我们用数据库建了一张play_list_tags表实现了歌单标签的增删改查功能。
本节我们建一张net_cache表,用来缓存一些主要界面的网络数据内容,这样当网络异常时可以用数据库中的缓存内容填充界面,不至于整个空白一片,优化用户的使用体验。
新建 Entity
我们新建一个NetCache,代码如下:
@Entity(tableName = "net_cache")
data class NetCache (
@PrimaryKey(autoGenerate = false)
val type: Int,
val content: String
)
NetCache 比较简单,表名为net_cache; 表包含两个字段,type表示网络请求数据的类型,content表示请求到的数据的JSON字符串。
@Dao
interface NetCacheDao {
@Query("SELECT * FROM net_cache WHERE type = :type")
suspend fun getNetCache(type: Int): NetCache
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertNetCache(cache: NetCache)
@Delete
suspend fun deleteNetCache(cache: NetCache)
@Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun updateNetCache(cache: NetCache)
}
新建 Dao
接下来我们新建一个 NetCacheDao。
@Dao
interface NetCacheDao {
@Query("SELECT * FROM net_cache WHERE type = :type")
suspend fun getNetCache(type: Int): NetCache
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertNetCache(cache: NetCache)
@Delete
suspend fun deleteNetCache(cache: NetCache)
@Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun updateNetCache(cache: NetCache)
}
其他的方法比较简单,重点说一下getNetCache方法,我们看到方法接收一个type参数,然后这个参数替换@Query的SQL语句中SELECT * FROM net_cache WHERE type = :type的占位符:type.
修改 Database
我们接下来修改下MusicDatabase
@Database(version = 1, entities = [PlayListTagResponse.PlayListTag::class, NetCache::class], exportSchema = false)
abstract class MusicDatabase : RoomDatabase() {
// ...
abstract fun netCacheDao(): NetCacheDao
}
在@Database的注解中的entities元素中增加NetCache::class
添加一个netCacheDao方法,这个方法返回NetCacheDao
修改 HomeViewModel
发现页有两个请求,一个请求Banner,一个请求其他Block信息。
两个请求
修改的主要代码如下:
// 1
class HomeViewModel(application: Application) : AndroidViewModel(application) {
// 2
private val netRepository : NetCacheRepository
init {
val netCacheDao = MusicDatabase.getInstance(application).netCacheDao()
netRepository = NetCacheRepository(netCacheDao)
fetchData()
}
// 请求数据
fun fetchData() {
try {
viewModelScope.launch(Dispatchers.IO) {
val bannerJob = async { HomeRepository.getHomeBanner() }
val blocksJob = async { HomeRepository.getHomeBlocks() }
var bannerResponse = bannerJob.await()
var blocksResponse = blocksJob.await()
if (bannerResponse.code != NetErrorType.UnSpecified && blocksResponse.code != NetErrorType.UnSpecified) {
withContext(Dispatchers.Main) {
_homeLiveData.value = Pair(bannerResponse.banners, blocksResponse.data)
}
// 3
updateBannerCache(bannerResponse)
updateBlockCache(blocksResponse)
return@launch
}
// 4
if (bannerResponse.code == NetErrorType.UnSpecified) {
val cache = getBannerCache()
cache?.let {
bannerResponse = it
}
}
if (blocksResponse.code == NetErrorType.UnSpecified) {
val cache = getBlockCache()
cache?.let {
blocksResponse = it
}
}
withContext(Dispatchers.Main) {
_homeLiveData.value = Pair(bannerResponse.banners, blocksResponse.data)
}
}
} catch (e: Exception) {
}
}
/* 存入Banner缓存 */
private suspend fun updateBannerCache(banners: BannerResponse) {
val jsonAdapter = Moshi.Builder().build().adapter(BannerResponse::class.java)
val jsonStr = jsonAdapter.toJson(banners)
netRepository.insertNetCache(NetCache(NetCacheType.HomeBanner, jsonStr))
}
/* 存入Blocks缓存 */
private suspend fun updateBlockCache(blocks: HomeResponse) {
val jsonAdapter = Moshi.Builder().build().adapter(HomeResponse::class.java)
val jsonStr = jsonAdapter.toJson(blocks)
netRepository.insertNetCache(NetCache(NetCacheType.Discovery, jsonStr))
}
/* 获取Banner缓存 */
private suspend fun getBannerCache(): BannerResponse? {
val cacheStr = netRepository.getNetCache(NetCacheType.HomeBanner) ?: return null
val jsonAdapter = Moshi.Builder().build().adapter(BannerResponse::class.java)
return jsonAdapter.fromJson(cacheStr.content)
}
/* 获取Block缓存 */
private suspend fun getBlockCache(): HomeResponse? {
val cacheStr = netRepository.getNetCache(NetCacheType.Discovery) ?: return null
val jsonAdapter = Moshi.Builder().build().adapter(HomeResponse::class.java)
return jsonAdapter.fromJson(cacheStr.content)
}
}
主要修改有:
为了获取到Application将ViewModel改为AndroidViewModel,
定义一个NetCacheRepository属性来操作数据库
获取到网络数据后存入数据库中,见updateBannerCache和updateBlockCache方法
如果没有获取到网络数据,就从数据库中获取。见getBannerCache和getBlockCache方法
到目前为止,代码已经修改完毕,运行程序,程序报错了。
报错内容为Room cannot vertify the data integrity. Looks like you've changed schema but forgot to update the version number.
错误
这是错误是因为我们添加了表,需要进行Migration
Migration
将 MusicDatabase的Version修改为2
@Database(version = 2, entities = [PlayListTagResponse.PlayListTag::class, NetCache::class], exportSchema = false)
新建一个Migration
private class Migration1To2: Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS net_cache" +
"('type' INTEGER NOT NULL PRIMARY KEY, 'content' TEXT NOT NULL)")
}
}
新建一个Migration1To2,继承自Migration,构造函数的1,2代表从版本1前移到版本2.
在migrate方法中执行建表操作,CREATE TABLE IF NOT EXISTS net_cache ('type' INTEGER NOT NULL PRIMARY KEY, 'content' TEXT NOT NULL)。
初始化MusicDatabase的时候调用addMigrations方法
val instance = Room.databaseBuilder(application, MusicDatabase::class.java, "music_database")
.addCallback(MusicDatabaseCallBack())
.addMigrations(Migration1To2())
.build()
运行程序,现在一切正常了。通过Migration我们就实现了页面网络请求的数据库缓存。
网络请求缓存
java migration_JetPack知识点实战系列九:Room数据库Migration相关推荐
- Java秒杀系统实战系列~数据库级别Sql的优化与代码的调整
摘要: 本篇博文是"Java秒杀系统实战系列文章"的第十三篇,从本篇文章开始我们将进入"秒杀代码优化"环节,本文将首先从数据库级别Sql的优化入手,结合调整秒杀 ...
- Java秒杀系统实战系列~RabbitMQ死信队列处理超时未支付的订单(转)
转自: https://juejin.cn/post/6844903903130042376 文末有源代码,非常棒 摘要: 本篇博文是"Java秒杀系统实战系列文章"的第十篇,本篇 ...
- Java秒杀系统实战系列~分布式唯一ID生成订单编号
摘要: 本篇博文是"Java秒杀系统实战系列文章"的第七篇,在本博文中我们将重点介绍 "在高并发,如秒杀的业务场景下如何生成全局唯一.趋势递增的订单编号",我们 ...
- Java秒杀系统实战系列~构建SpringBoot多模块项目
摘要:本篇博文是"Java秒杀系统实战系列文章"的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项 ...
- Java秒杀系统实战系列~JMeter压力测试重现秒杀场景中超卖等问题
摘要: 本篇博文是"Java秒杀系统实战系列文章"的第十二篇,本篇博文我们将借助压力测试工具Jmeter重现秒杀场景(高并发场景)下出现的各种典型的问题,其中最为经典的当属&quo ...
- Java秒杀系统实战系列~基于Redisson的分布式锁优化秒杀逻辑
摘要: 本篇博文是"Java秒杀系统实战系列文章"的第十五篇,本文我们将借助综合中间件Redisson优化"秒杀系统中秒杀的核心业务逻辑",解决Redis的原子 ...
- Java秒杀系统实战系列~定时任务补充处理超时未支付的订单
摘要: 本篇博文是"Java秒杀系统实战系列文章"的第十一篇,本篇博文我们将借助定时任务调度组件来辅助"失效超时未支付的订单记录"的处理,用以解决上篇博文中采用 ...
- Java秒杀系统实战系列~商品秒杀代码实战
摘要: 本篇博文是"Java秒杀系统实战系列文章"的第六篇,本篇博文我们将进入整个秒杀系统核心功能模块的代码开发,即"商品秒杀"功能模块的代码实战. 内容: & ...
- JAVA面试常考系列九
转载自 JAVA面试常考系列九 题目一 RMI架构层的结构是如何组成的? RMI体系结构由三层组成,分别是: 存根和骨架层(Stub and Skeleton Layer) 远程引用层(Remote ...
- Java基础复习笔记系列 九 网络编程
Java基础复习笔记系列之 网络编程 学习资料参考: 1.http://www.icoolxue.com/ 2. 1.网络编程的基础概念. TCP/IP协议:Socket编程:IP地址. 中国和美国之 ...
最新文章
- oracle循环语句loop,oracle循环语句loop
- [LeetCode]Gray Code
- jquery checkbox attr区别prop
- Linux驱动中获取系统时间
- 机器找不到 libcudnn.so.6
- java输出到文本_java怎么把运行结果写到一个自动生成的文本里?。能给个例子么,详细点...
- 云服务器更换系统要钱吗,云服务器可以更换系统吗
- 爬虫实例十三 教你怎么用爬虫一次给女朋友拿下28万张情侣头像
- php 获取小数精度,php小数精度问题
- Android开发视频教程汇总
- 从电信的广告学习情景与文案的搭配
- python中 {0:2.2f}与{1:2.2f}的区别
- RGB转换HSL,HSV及切割车牌
- 关于 Kubernetes集群中仪表盘(dashboardKuboard)安装的一些笔记
- pyinstaller打包exe加入版本和版权信息
- LabVIEW基础(1)
- CMake的一些细节
- 如何从iCloud共享iWork文档
- paypay+thinkphp开发接入网站
- 狐狸抓兔子(实验楼的练习)
热门文章
- stm32视频教程分享:心率检测仪的设计与实现
- 【EduCoder答案】HTML——表单类的标签
- 利用线性回归预测波士顿房价
- Gym 100015B Ball Painting
- Ubuntu18.04系统下装CUDA9.0
- 小程序获取附近IBeacon设备
- 【开源编码分享】Python古三式∶太乙神数丶奇门遁甲丶大六壬
- 美通社企业新闻汇总 | 2019.3.7 | 百胜中国在上海设创新中心;折叠手机2019年预计仅占智能手机市场渗透率0.1%...
- 【对比Java学Kotlin】类型别名
- 并发编程之四:并发之共享问题、线程安全、synchronized关键字