room数据库升级

room数据库升级

一、操作步骤说明

  1. 增加数据库版本号

    @Database注解中增加版本号(version),比如从version 1升级到version 2。

    @Database(entities = [Song::class,],**version = 1**,//1->2
    )
    abstract class AppDataBase : RoomDatabase() {
    }
    
  2. 定义数据库变化

    根据需要修改的内容(添加表、修改表、删除表等),更新对应的Entity类和DAO接口

  3. 创建Migration对象

    • 创建一个Migration对象,该对象定义了数据库从旧版本升级到新版本时需要执行的操作。
    • 实现migrate()方法,编写SQL语句来处理结构变化或数据迁移
    private val MIGRATION_1_2 = object : Migration(1, 2) {override fun migrate(database: SupportSQLiteDatabase) {//升级操作}}        
    
  4. 配置数据库实例

    • 在创建Room数据库实例时,通过.addMigrations()方法添加上一步创建的Migration对象。
    • 如果有多个版本的迁移,可以链式调用.addMigrations()添加多个Migration对象。
    var appDatabase = Room.databaseBuilder(context,AppDataBase::class.java,DATABASE_NAME,).apply {// 把初始下载权限表放这里了createFromAsset("init.db")addMigrations(MIGRATION_1_2, )}
    
  5. 测试迁移

    • 使用单元测试来确保Migration正确无误地执行了预期的数据库变化。
    • 测试包括但不限于表结构变化、数据迁移的正确性、数据完整性等。

二、常见升级

表定义

@Entity(tableName = "t_song")
data class Song(@PrimaryKey@ColumnInfo(name = "song_id")val songId: String,//歌曲id@ColumnInfo(name = "name")val songName: String,//歌曲名称@ColumnInfo(name = "type")val songType: Int,//歌曲类型:1:歌曲 2:听书
)

2.1 增加一个普通字段、索引

2.1.1 Entity类修改

@Entity(tableName = "t_song")
data class Song(@PrimaryKey@ColumnInfo(name = "song_id")val songId: String,//歌曲id@ColumnInfo(name = "name")val songName: String,//歌曲名称@ColumnInfo(name = "type")val songType: Int,//歌曲类型:1:歌曲 2:听书//新增albumId字段,且创建索引@ColumnInfo(name = "album_id",index = true)val albumId: String?=null,//专辑id
)

2.1.1 旧版本升级兼容即创建Migration对象,

private val MIGRATION_1_2 = object : Migration(1, 2) {override fun migrate(database: SupportSQLiteDatabase) {//升级操作//增加url字段database.execSQL("ALTER TABLE t_songs ADD COLUMN `album_id` TEXT")//增加索引database.execSQL("CREATE INDEX IF NOT EXISTS `index_t_songs_album_id` ON `t_songs` (`album_id`)")}}

2.2 删除表

2.2.1 移除Entity和DAO

  • 从代码中移除@Entity注解的Song类(假设Song是对应t_songs表的Entity)。
  • 同时移除与Song类相关的DAO接口。

2.2.2 删除旧表

private val MIGRATION_1_2 = object : Migration(1, 2) {override fun migrate(database: SupportSQLiteDatabase) {//升级操作// 删除t_songs表database.execSQL("DROP TABLE IF EXISTS t_songs")}}

t_songs表数据有几百万条时,执行DROP TABLE IF EXISTS t_songs 会很耗时,经测试400w条时就会达到十几秒。在升级时会阻塞数据库的操作进而影响业务的处理,很可能导致UI界面加载不出数据一直转圈圈。所以我们可以采用在数据库升级时即migrate()不删除废弃的表,而是在业务中:

  1. 开启一个线程,每1000条的删除数据;
  2. 当数据删除完后,删除t_songs表

代码如下:

class MainViewModel{@Injectlateinit var appDataBase: Lazy<AppDataBase>,fun deleteTSong(){viewModelScope.launch(Dispatchers.IO) {val count = getTSongsCount()if(count >= 0){val page = count / 1000 + 1//大概率不会整除,直接+1val writableDatabase: SupportSQLiteDatabase = appDataBase.get().openHelper.writableDatabaserepeat(page) {//这里的分页删除好傻,耗时毫秒级writableDatabase.execSQL("DELETE FROM t_songs WHERE song_id IN (SELECT song_id FROM t_songs LIMIT 1000)")//重点 这里延迟120ms,是为了这里删除不要独占数据库操作,,如果不理解 想想cpu时间片delay(120)}//没数据了就删除表appDataBase.get().openHelper.writableDatabase.execSQL("DROP TABLE IF EXISTS t_songs"}}}/*** 获取t_songs表大小*/private suspend fun getTSongsCount(): Int {return withContext(Dispatchers.IO) {var count = -1runCatching{appDataBase.get().query("SELECT count(*) FROM t_songs_temp", null).use {if (it.moveToFirst()) {count = it.getInt(0)}}} count}}
}

2.3 修改字段名或者类型或者增加主键

针对这种不能在旧表上修改的需求,我们只能新建一个新表然后把旧表中的数据复制到新表中——销毁重新。

例如,我们的t_songs 增加了一个曲库比如tme, 这时song_id就可能在两个曲库中重复了,所以要增加sourceId表示曲库id,song_id + source_id 一起作为主键。

2.3.1 修改Entity

@Entity(tableName = "t_song", primaryKeys = ["song_id","song_type","source_id"])
data class Song(@ColumnInfo(name = "song_id")val songId: String,//歌曲id@ColumnInfo(name = "name")val songName: String,//歌曲名称@ColumnInfo(name = "type")val songType: Int,//歌曲类型:1:歌曲 2:听书//增加source_id@ColumnInfo(name = "source_id")val sourceId: Int,//曲库id)

2.3.2 旧版本升级兼容——销毁重建

有两种方案:

方案一、

  1. 创建一个新表:创建一个新表,其结构与原表相同,除了需要修改的字段名。
  2. 复制数据:将原表中的数据复制到新表中,同时将需要修改的字段名的数据赋值到新的列名。
  3. 删除原表:删除原始的表。
  4. 重命名新表:将新表重命名为原始表的名字。

方案二、

  1. 重命名旧表:将原始表重命名为t_songs_old
  2. 创建一个新表:创建一个新表,其结构与原表相同,除了需要修改的字段名。
  3. 复制数据:将原表中的数据复制到新表中,同时将需要修改的字段名的数据赋值到新的列名。
  4. 删除原表:删除原始的表。

我们可以看到方案二如果不执行第4步,我们可以通过App Inspection查看迁移前后的表方便我们调试,还有就是第4步 如果旧表中数据量很大时可以把步骤4放在业务中进行慢慢删除,综上 推荐方案二。

private val MIGRATION_1_2 = object : Migration(1, 2) {override fun migrate(database: SupportSQLiteDatabase) {//1. 重命名旧表database.execSQL("ALTER TABLE t_songs RENAME TO t_songs_temp")//2. 如果有索引,则删除旧表的索引database.execSQL("DROP INDEX index_t_songs_album_id")//3. 创建新表,这里的语句建议查看自动生成AppData_Imp类里的代码,复制过来,防止自己写错了database.execSQL("CREATE TABLE IF NOT EXISTS `t_song` (`song_id` TEXT NOT NULL, `song_id` TEXT NOT NULL, `name` TEXT NOT NULL,  `source_id` INTEGER NOT NULL, PRIMARY KEY(`song_id, `source_id`))")//4. 如果旧表有索引,则再建一个索引database.execSQL("CREATE INDEX IF NOT EXISTS `index_t_songs_album_id` ON `t_songs` (`album_id`)")//5.复制数据database.execSQL("INSERT OR REPLACE INTO t_songs (song_id,song_name,song_type,source_id) SELECT song_id,song_name,song_type,0 AS source_id FROM t_songs_old")//6.删除原表database.execSQL("DROP TABLE IF EXISTS t_songs_old")}
}

如果第6点很耗时建议参考2.2节,如果数据量很大第5步也会很耗时,建议只迁移表中有用的数据(有时候业务中只会把数据缓存到表中不删除,这是表中有用数据很少,建议联表进行查询出有用数据进行迁移)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/733038.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个爬虫自动化数据采集的故事~

目录 一、原文二、故事前半段背景内容三、正经的讲点DrissionPage知识四、故事的收尾 一、原文 原文来自一个爬虫自动化数据采集的故事~ , 建议点击链接看文章末尾的视频笔者不擅长自动化&#xff0c;一个小小故事分享给大家&#xff0c;仅个人观点 二、故事前半段背景内容 …

剑指offer面试算法题目,自己总结的

JZ31 栈的压入、弹出序列-C++-CSDN博客 剑指 Offer(C++版本)系列:从尾到头打印单链表(C++)-CSDN博客 剑指offer》15--二进制中1的个数[C++]-CSDN博客 《剑指offer》14--剪绳子(整数拆分)[C++]-CSDN博客 剑指 Offer 12. 矩阵中的路径-CSDN博客 C++--机器人的运动范围…

IP-guard邮件管控再升级,记录屏幕画面,智能阻断泄密邮件

邮件是工作沟通以及文件传输的重要工具,却也成为了信息泄露的常见渠道。员工通过邮件对外发送了什么内容,是否含有敏感信息都无从得知,机密通过邮件渠道外泄也难以制止。想要防止企业的重要信息通过邮件方式泄露,我们不仅需要通过技术措施对外发邮件的行为进行规范,也要对…

使用大带宽服务器对网站有什么好处?

近年来大带宽服务器频频出现在咱们的视野当中&#xff0c;选用的用户也在与日增长。那么究其主要原因是什么?租用大带宽服务器的好处又有哪些? 今天德迅云安全带您来了解下。1.有效提升网站访问速度 一般来说&#xff0c;正规的网站对用户体验度都是非常有讲究的&#xff0c;…

L-2:插松枝(Python)

作者 陈越 单位 浙江大学 人造松枝加工场的工人需要将各种尺寸的塑料松针插到松枝干上&#xff0c;做成大大小小的松枝。他们的工作流程&#xff08;并不&#xff09;是这样的&#xff1a; 每人手边有一只小盒子&#xff0c;初始状态为空。每人面前有用不完的松枝干和一个推送…

命令行中当前目录下打开资源管理器窗口

有时候使用命令行时执行命令生成了一些文件&#xff0c;想在资源管理器窗口中打开&#xff0c;或者向当前位置放入文件&#xff0c;以供处理。往往需要打开当前目录的资源管理器窗口&#xff0c;通常就是文件资源管理器&#xff0c;或者我的电脑一层层找到当前目录&#xff0c;…

手机号验证码重新发送

前文叙述 很久以前做的一个 demo &#xff0c;纯 HTML 、CSS、js 制作&#xff0c;一定时间段之后才可以重新发送验证码&#xff0c;如 60s 后再次发送验证码&#xff0c;在该时间段内发送验证码按钮为禁用状态&#xff0c;实战开发过程也亦是同理&#xff0c;因此记录一手。 一…

内存的基本特性

初识内存 1&#xff0c;内存的基本特性 现代计算机的基本组成 现代计算机之父--冯诺伊曼提出了计算机的基本组成&#xff1a; 运算器&#xff1a;负责算术运算和逻辑运算&#xff0c;目前已经集成到CPU中。 控制器&#xff1a;负责控制系统的各部件&#xff0c;使之协调的…

【笔记】Android ServiceStateTracker 网络状态变化逻辑及SPN更新影响

业务简介 在网络状态变化的时候&#xff08;数据或WiFi&#xff09;&#xff0c;会更新SPN。 基于Android U的代码分析。 分类&#xff1a;SPN Data_Dic-的博客-CSDN博客 功能逻辑 状态说明 飞行模式下注册上WFC的话&#xff0c;注册状态MD上报 regState: NOT_REG_MT_NOT…

IO进线程练习(用到了:文件IO 标准IO 多进程 exec进程转移 有名管道 无名管道)

1 利用文件IO读取文件数据存入链表&#xff0c;当触法ctrlc时将链表数据存入文件。 main.c #include"head.h" FILE*fp_w; linklist L; void handler(int sig){out_file(fp_w,L);printf("文件写入完成\n");exit(0); }int main(int argc, const char *argv…

一文读懂MySQL7大日志(slow、redo、undo、bin、relay、general、error)

Slow Log 简介 用于记录执行时间超过指定值的 SQL 语句的详细信息&#xff0c;多用于调试和监控。 配置 因为开启会略微影响性能&#xff0c;所以默认没有开启&#xff0c;所以需要配置。 查看是否开启 show variables like %slow%; ------------------------------------…

计算机找不到api-ms-win-core-path-l1-1-0的5种解决方法

在计算机使用过程中&#xff0c;我们可能会遇到各种问题&#xff0c;其中之一就是找不到某些系统文件。最近&#xff0c;许多用户反映他们在使用电脑时遇到了“找不到api-ms-win-core-path-l1-1-0文件”的问题。这个问题通常出现在Windows操作系统中&#xff0c;可能会影响到一…

Java外观模式源码剖析及使用场景

外观模式 一、介绍二、家庭影院项目案例使用三、Java API或框架中应用分析三、Spring框架ApplicationContext源码 一、介绍 外观模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供了一个统一的高层接口,使得子系统更加容易使用。外观模式定义了一个高层接…

leetcode:二叉树的左右子树反转的递归和迭代的C++实现

问题描述 给定一个二叉树&#xff0c;将其每个节点的左右子树进行反转。 解决方案 以下是 C 代码实现&#xff1a; TreeNode* invertTree(TreeNode* root) {if (root nullptr) {return nullptr;}// 交换当前节点的左右子树TreeNode* temp root->left;root->left r…

C语言转义字符:一文打尽

转义字符 1. 前言2. 预备知识2.1 打印格式2.2 进制转换2.3 ASCII码 3. 什么是转义字符4. 常见的转义字符4.1 \?4.2 \4.3 \"4.4 \\4.5 \dddddd表示1到3个八进制数字4.6 \xdddd表示1到2个十六进制数字4.7 其他转义字符 5. 一道笔试题6. 一个小插曲 1. 前言 大家好&#xf…

DFS和BFS以及练习题目(未完待续)

DFS和BFS 温馨提示&#xff1a;学习dfs之前最好先了解一下递归的思想。 递归思想 斐波那契 题目分析 题目代码 import java.util.Scanner; public class Main{static long dp[]; public static void main(String[] args) {Scanner scanner new Scanner(System.in);int t…

吴恩达deeplearning.ai:倾斜数据集的误差指标精确率、召回率

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 文章目录 倾斜数据集的误差指标罕见病预测精确率和召回率 精确率和召回率的权衡精确率和召回率的矛盾关系 F1算法 倾斜数据集的误差指标 在神经网络中&#xff0c;如果你的数据集中正例和负…

CSS样式中长度单位含义解析:rpx、px、vw、vh、em、rem、pt

在 CSS 样式中&#xff0c;有几种常见的长度单位&#xff0c;包括 rpx 、 px 、 vw 和 vh 等&#xff0c;含义解析如下&#xff1a; 1 . rpx &#xff08;响应像素&#xff09;&#xff1a; 是微信小程序中的一种相对长度单位&#xff0c;可以根据屏幕宽度进行自适应缩放。 1rp…

PTA 对于下列程序,正确的是() 。void f(int *p){ *p = 5;}int main(void){ int a, *p; a = 10;

对于下列程序&#xff0c;正确的是&#xff08;&#xff09; 。 void f(int *p) {*p 5; } int main(void) {int a, *p;a 10;p &a;f(p);printf(“%d”, (*p));return 0; }A.5 B.6 C.10 D.11 答&#xff1a;A 解析&#xff1a;这里考察当是指针作为函数的参数。这里将 p …

python脚本批量关闭exe文件

python脚本批量关闭exe文件 1、安装psutil库 pip install psutil2、示例代码 """ @contact: 微信 1257309054 @file: main.py @time: 2024/3/9 21:16 @author: LDC """ import os import time import psutildef is_process_running(process_n…