【数据库】sqlite版本升级、降级

2023-11-02

参考:https://www.jianshu.com/p/65923fa3e3dc

1 正常全部使用流程

1.1 定义全局变量

public static SQLiteHelper dbHelper;
public static String folder = "android.xxx.xxx"; // 数据库保存地址
public static String file = "database.db"; // 数据库名称
public static int DB_VERSION = 1; // 数据库的版本号
public static SQLiteDatabase db; // 数据库

1.2 初始化数据库(在MainActivity中或者Services中)

这样得到的数据库保存在公共文件夹,即使卸载APP,数据库也不会丢失。

static void init_db(Context context) {
    String pathname = GetDir.getDir(Constant.folder);
    File file = new File(pathname, Constant.file);
    try {
        if(!file.exists()){
            file.createNewFile();
            Uri uri = Uri.fromFile(file);
            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
            context.sendBroadcast(intent);
        }
    } catch (IOException e) {
        Log.v("main", "failed1");
        e.printStackTrace();
    }
    try {
        Constant.dbHelper = new SQLiteHelper(context, pathname+"/"+Constant.file, null, Constant.DB_VERSION);
        Constant.db = Constant.dbHelper.getWritableDatabase();    // 调用SQLiteHelper.OnCreate()
        Log.v("main", "new db");
    } catch (IllegalArgumentException e) {
        Log.v("main", "failed2");
        e.printStackTrace();
        Constant.dbHelper.onUpgrade(Constant.db, Constant.DB_VERSION - 1, Constant.DB_VERSION);
    }
}

其中GetDir.getDir如下:

public class GetDir {
    public static String getDir(String pathname) {
        String sdcardPath = Environment.getExternalStorageDirectory().toString();
        File dir = new File(sdcardPath + File.separator + pathname /*+ File.separator + "Files"*/);
        if (dir.exists()) {
            return dir.toString();
        } else {
            dir.mkdirs();
            return dir.toString();
        }
    }
}

1.3 在SQLiteHelper中定义数据库初始化的步骤,数据库升级和降级的操作

public class SQLiteHelper extends SQLiteOpenHelper {
	static  final String TAG = "SQLiteHelper";

	public SQLiteHelper(Context context, String name, CursorFactory factory, int version) {
		super(context, name, factory, version);
	}
	
	/**
	 * 创建新表
	 */
	@Override
	public void onCreate(SQLiteDatabase db) {
		Log.v(TAG,"onCreate");
		db.execSQL("CREATE TABLE IF NOT EXISTS mytable (name varchar(15), age varchar(5))");
	}

	@Override
	public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.v(TAG,"onDowngrade");
		if(oldVersion == 3 && newVersion == 2){ // 从3降到2
			Log.i(TAG, "onDowngrade: 从3降到2");
			db.execSQL("create table tmp_mytable as select name, age from mytable"); // 根据旧表创建新表,相当于删除score列
			db.execSQL("drop table mytable");
			db.execSQL("alter table tmp_mytable rename to mytable");
		}
	}

	/**
	 * 当检测与前一次创建的数据库版本不一样时,先删除表再创建新表
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.v(TAG,"onUpgrade");
		switch (newVersion){
			case 2: { // 给表添加score字段,并赋初值0
				Log.i(TAG, "onUpgrade: newVersion = 2");

				// 方式1
				db.execSQL("alter table mytable add score varchar(5)");
				ContentValues values = new ContentValues();
				values.put("score", "0");
				db.update("mytable ", values, "name!=?", new String[]{""});


				// 方式2
				// 先创建符合要求的临时表			
				db.execSQL("CREATE TABLE IF NOT EXISTS mytable (name varchar(15), age varchar(5), score varchar(5))");
				
				// 将数据从旧表复制到临时表
				ContentValues values = new ContentValues();
				values.put("score", "0");
				db.execSQL("INSERT INTO tmp_mytable  (name, age) SELECT name, age from mytable");
				db.update("tmp_mytable", values, "name!=?", new String[]{""}); // 只要name不为空,都修改score为0

				// 删除旧表
				db.execSQL("DROP TABLE IF EXISTS mytable");

				// 将临时表重命名为旧表名
				db.execSQL("ALTER TABLE tmp_mytable RENAME TO mytable");
				break;
			}

			case 3: { // 删掉两个表中的region字段
				Log.i(TAG, "onUpgrade: newVersion = 3");
				db.execSQL("create table tmp_mytable as select name, age from mytable"); //  若写 where 1 = 2 则只会复制表结构,不复制内容
				db.execSQL("drop table mytable");
				db.execSQL("alter table tmp_mytable rename to mytable");
				break;
			}
			
			default:
				break;
		}
	}
}

2. sqlite版本升级

2.1 方法1 硬升级 不推荐

首先是一种硬升级的方法,删除所有的所有旧表,然后重新创建数据库中的所有表。这种方法不够优雅,删除数据存在无法恢复的风险,不推荐这种方式。

@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// 硬升级
		db.execSQL("DROP TABLE IF EXISTS numbers");
		db.execSQL("DROP TABLE IF EXISTS recordInfo");
		onCreate(db);

2.2 推荐方法2种如下

/**
	 * 当检测与前一次创建的数据库版本不一样时,先删除表再创建新表
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.v(TAG,"onUpgrade");
		switch (newVersion){
			case 2: { // 给表添加score字段,并赋初值0
				Log.i(TAG, "onUpgrade: newVersion = 2");

				// 方式1
				// 先检查是否已经有score字段,如果没有,才给它升级
				ContentValues values = new ContentValues();
				values.put("score", "0");
				if(!checkColumnExist(db, "mytable", "score")){
					db.execSQL("alter table mytable add score varchar(5)");
					db.update("mytable", values, "name!=?", new String[]{""});
				}


				// 方式2
				// 先创建符合要求的临时表			
				db.execSQL("CREATE TABLE IF NOT EXISTS mytable (name varchar(15), age varchar(5), score varchar(5))");
				
				// 将数据从旧表复制到临时表
				ContentValues values = new ContentValues();
				values.put("score", "0");
				db.execSQL("INSERT INTO tmp_mytable  (name, age) SELECT name, age from mytable");
				db.update("tmp_mytable", values, "name!=?", new String[]{""}); // 只要name不为空,都修改score为0

				// 删除旧表
				db.execSQL("DROP TABLE IF EXISTS mytable");

				// 将临时表重命名为旧表名
				db.execSQL("ALTER TABLE tmp_mytable RENAME TO mytable");
				break;
			}

			case 3: { // 删掉两个表中的region字段
				Log.i(TAG, "onUpgrade: newVersion = 3");
				db.execSQL("create table tmp_mytable as select name, age from mytable"); //  若写 where 1 = 2 则只会复制表结构,不复制内容
				db.execSQL("drop table mytable");
				db.execSQL("alter table tmp_mytable rename to mytable");
				break;
			}
			
			default:
				break;
		}
	}
}

检查某表列是否存在

/**
 * 检查某表列是否存在
 * @param db
 * @param tableName 表名
 * @param columnName 列名
 * @return
 */
private boolean checkColumnExist(SQLiteDatabase db, String tableName, String columnName) {
	boolean result = false ;
	Cursor cursor = null ;
	try{
		//查询一行
		cursor = db.rawQuery( "SELECT * FROM " + tableName + " LIMIT 0", null );
		result = cursor != null && cursor.getColumnIndex(columnName) != -1 ;
	}catch (Exception e){
		Log.e(TAG,"checkColumnExists1..." + e.getMessage()) ;
	}finally{
		if(null != cursor && !cursor.isClosed()){
			cursor.close() ;
		}
	}

	return result ;
}

2. 降级

@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
	if(oldVersion == 3 && newVersion == 2){ // 从3降到2
		Log.i(TAG, "onDowngrade: 从3降到2");

		db.execSQL("create table tmp_mytable as select name, age from mytable");
		db.execSQL("drop table mytable");
		db.execSQL("alter table tmp_tb1 rename to mytable");
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据库】sqlite版本升级、降级 的相关文章

  • 图像作为电子邮件附件

    我想构建一个应用程序 我可以在电子邮件中附加图像 打开图像并将其设置为我的壁纸 我想让它跨平台 所以你能告诉我是否可以使用phonegap 或者我是否必须为iphone和android构建一个本机应用程序 您好 如果您只想通过电子邮件附加图
  • 从 BroadcastReceiver 类调用活动方法

    我知道我可以做一个内部接收器类来调用接收器中的任何方法 但我的主要活动太大了 要做的事情也很多 因此 我需要一个扩展广播接收器的类 但它不是内部类 并且可以从我的主要活动中调用一种方法 我不知道是否可能 但我的活动是家庭活动和 single
  • Android 构建发布失败,原因为:java.lang.ArrayIndexOutOfBoundsException:213(pr​​oguard 问题)

    我的项目使用调试构建变体构建得很好 但使用发布变体 Android Studio 会抛出 引起原因 java lang ArrayIndexOutOfBoundsException 213 可能是什么问题 如果我设置minifyEnable
  • logcat 中 mSecurityInputMethodService 为 null

    我写了一点android应显示智能手机当前位置 最后已知位置 的应用程序 尽管我复制了示例代码 并尝试了其他几种解决方案 但似乎每次都有相同的错误 我的应用程序由一个按钮组成 按下按钮应该log经度和纬度 但仅对数 mSecurityInp
  • 为什么 Java 8 不允许非公共默认方法?

    让我们举个例子 public interface Testerface default public String example return Hello public class Tester implements Testerface
  • java for windows 中的文件图标叠加

    我正在尝试像 Tortoise SVN 或 Dropbox 一样在文件和文件夹上实现图标叠加 我在网上查了很多资料 但没有找到Java的解决方案 Can anyone help me with this 很抱歉确认您的担忧 但这无法在 Ja
  • Android 中如何通过彩信发送图片?

    我正在开发多媒体应用程序 我正在通过相机捕获一张图像 并希望将该图像和文本发送到其他号码 但我不知道如何通过彩信发送图像 MMS 只是一个 http post 请求 您应该使用执行请求额外的网络功能 final ConnectivityMa
  • 关键字“table”附近的语法不正确,无法提取结果集

    我使用 SQL Server 创建了一个项目 其中包含以下文件 UserDAO java public class UserDAO private static SessionFactory sessionFactory static se
  • Flutter 中有预填充数据库使用的示例吗?

    Flutter 中有预填充数据库使用的示例吗 我不需要 CRUD 示例 此时我只需要从数据库读取数据即可 我是 Flutter 新手 所以一步一步的教程会很好 您可以将您的应用程序与预填充的 sqlite 数据库捆绑在一起assets文件夹
  • 我应该释放或重置 MediaPlayer 吗?

    我有自己的自定义适配器类 称为 WordAdapter 并且我正在使用媒体播放器 名为pronounce WordAdapter 类中的全局变量 我有不同的活动 其中每个列表项都有线性布局 名为linearLayout 我正在设置onCli
  • Dagger 2 没有生成我的组件类

    我正在使用 Dagger 2 创建我的依赖注入 几个小时前它还在工作 但现在不再生成组件 这是我创建组件的地方 public class App extends Application CacheComponent mCacheCompon
  • 在命令行上卸载 Android SDK 的选定部分

    这与 卸载旧的 Android SDK 版本 https stackoverflow com questions 15182377 uninstall old android sdk versions 除非我想在无头 Linux CI 服务
  • 如何检查 Android 中的同步设置

    我正在构建一个 Android 应用程序 我需要检查设备中注册的每个单独帐户的同步设置 我知道我可以通过 ContentResolver 类来做到这一点 但我遇到了一些问题 我已设法获取设备上所有帐户的列表 但我不知道在运行时从哪里获取特定
  • 通过电子邮件发送文本文件附件

    我正在尝试附加一个文本文件以便通过电子邮件发送 但每当我打开电子邮件应用程序时 它都会说该文件不存在 请帮助 Intent i new Intent Intent ACTION SEND i setType text plain i put
  • 没有支持 FEATURE_CAMERA_EXTERNAL 的 Android 设备

    根据this doc https source android com devices camera external usb cameras一些 Android 设备允许使用 Camera2 API 访问外部 USB 摄像头 我检查了大约
  • 如何使用 AccessibilityService 在 Android 中模拟按键

    我正在编写一个辅助服务 我一直在尝试在应用程序上进行一些自动搜索 我使用accessibilityservice action paste来填充EditText 然后我需要模拟软键盘上的按键 但我不知道如何做 你们能帮我一下吗 你可以尝试A
  • 我的应用程序中的后退按钮出现问题[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想在手机关闭时清除共享首选项值 你
  • Dagger 2 中“HasFragmentInjector”的实际用法是什么

    我之前已经实现了 dagger2 v2 2 但现在他们也添加了 dagger android 部分 所以我正在用它创建示例项目 我知道旧的方法论 Provide and Modules and 成分等注释 但从 Dagger 2 8 开始
  • 发布的 Android apk 出现错误“包文件未正确签名”

    我最近将我的应用程序上传到 Android 市场 但是由于错误 下载时它拒绝运行 包文件未正确签名 我首先使用 eclipse 发布了数据包 右键单击导出 创建密钥库然后发布 但它拒绝工作 然后我下载了 keytool 和 jarsigne
  • Android 屏幕方向错误

    我使用的是 Android HTC HERO 2 1 版本 我写的活动

随机推荐

  • Python 微信机器人

    使用python构造一个微信聊天机器人 最近在学python的过程中无意间发现一个python库 wxpy 其可以实现让微信自动接收 处理消息并进行回复的一系列功能 感觉挺有意思的 便自行摸索学习 并成功地实现了其功能 故写下此博客作学习分
  • 谷歌浏览器插件Automa(入门,编写中,开专栏填坑中)

    谷歌浏览器插件Automa 入门 编写中 0 待成长的无代码化爬虫 1快速入门模块 1 1 中文设置 1 2 定位你想要操作的位置 1 3 进行操作 1 3 1 点击 1 3 2 输入 1 4 官方案例实战教学 1 4 1 百歌一下 1 4
  • spring boot 2.0.3 mybatis升级mybatis-plus

    项目原来是使用mybtais durid 现升级为mybatis plus2 3 durid mysql 官方文档地址 请注意 mybatis plus3 0 的配置有变化 请查看官网文档 可能是旧的 或demo 步骤如下 1 引入pom
  • Linux中文件的传输

    文件在系统中的传输 1 scp 上传 scp 本地文件 远程主机用户 远程主机ip 远程主机目录 步骤如下 下载 scp 远程主机用户 远程主机ip 远程主机目录 本地目录 步骤如下 2 rsync 远程同步 速度快 默认会忽略 文件属性
  • 【AnyQ】遇到的问题整理(一)

    一 编译出错 问题描述 In file included from home jockeyyan git repository QuestionAnsweringBot AnyQ build third party paddle src e
  • 分布式计算的基本原理

    author skate time 2010 03 08 从最近几次MMI设计会议讨论的结果来看 嵌入式程序员对于分布式计算知之甚少 他们对分布式计算有种恐惧 所以对分布式架构极力排斥 而他们的人数又占绝对优势 讨论N次 MMI的架构还是没
  • Jupyter Notebook 工作环境配置

    目录 背景 为什么要配置jupyter的工作环境呢 因为可以更方便的找到自己写的脚本所在的文件 步骤 1 新建一个文件夹 随便在哪里建 例如我在D盘新建了一个文件夹 D pythonworkspace 2 接下来配置环境变量 新建一个变量W
  • mysql的sql语句获取两点之间的距离

    一张表 表的经度和维度都为double或者decimal类型 传入参数经度116 366216 纬度39 939834 SELECT ROUND 6378 138 2 ASIN SQRT POW SIN 40 0497810000 PI 1
  • 真难!!!Java初中级岗位都能被卷到怀疑人生....

    Java一个初中级岗位有上千人同时竞争 内卷程度简直怀疑人生 最近不少群友吐槽 初中级岗位的面试简直是地狱级难度 面试官对常用框架的考察扣的都很细 但是在日常开发中又很难注意到这些细节 导致吃了很多亏 其实说到底 还是这个问题 主流框架都熟
  • SQL查询重复数据出现的次数

    背景 关系型数据库中 有这样一种情况 假设用户信息表中有一列idCard字段 该字段用来存储用户的身份证号 现在导入进来一批数据后有人告诉你库里存在重复的数据 那么此时 你需要重复的规则是什么 到底是哪列关键数据出现了重复 首先人名重复的情
  • JVM - 的类加载器(类加载子系统)

    文章目录 类加载子系统 Class Loader 作用 类加载过程 加载阶段 Loding 连接阶段 Linking 验证 Verification 准备 Preparation 解析 Resolution 初始化 Initializati
  • InsightFace_Pytorch人脸识别项目部署运行

    一 下载InsightFace Pytorch master 二 导入各种包 三 运行take pic py搜集样本 四 运行face verify py识别 遇到问题解决方法 1 导包失败 需使用python3 7版本 可以安装好 2 找
  • 2023最新C语言经典面试题汇总

    写出Bool int 指针变量 float与 零值 比较的if语句 Bool型 if flag if flag int型 if flag 0 if flag 0 指针变量 if p NULL if p NULL float型 const f
  • [机器学习与scikit-learn-49]:特征工程-特征选择(降维)-4-二级过滤-特征值与标签之间的关系:卡方过滤

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 124073917 目录 前言 第1章
  • android软件开发!Jetpack-MVVM-高频提问和解答,附带学习经验

    感悟 这个世界有一个 二八原则 在好多地方都发挥着作用 在Android开发上我认为也一样有用 做一个Android开发 你也许只会用到Android开发知识中的20 有80 其实你学了也不一定会用 而面试官也一样 他也可能只掌握了20 的
  • java通过JdbcTemplate连接多个(2个以上)不同类型的数据库

    1 业务场景 要求获取不同数据库的表信息和表结构信息 数据库类型包括oracle MySQL SqlServer 2 实现思路 step1 新增数据库连接信息 主机 端口 数据库类型 实例名 用户名 密码 状态 step2 测试连接 成功状
  • Python中常用的设计模式

    主要参考网址 http www pythontip com pythonPatterns 创建型模式 1 抽象工厂模式 class PetShop def init self animal factory None self pet fac
  • openstack-helm

    文章目录 openstack helm 安装 helm helm 初始化 openstack helm下载 openstack组件安装 安装ingress 安装ceph openstack ceph Mariadb RabbitMQ Mem
  • 好用免费的api接口大全

    API Application Programming Interface 应用程序接口 是一些预先定义的函数 或指软件系统不同组成部分衔接的约定 目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力 而又无需访问原码 或理解
  • 【数据库】sqlite版本升级、降级

    参考 https www jianshu com p 65923fa3e3dc 1 正常全部使用流程 1 1 定义全局变量 public static SQLiteHelper dbHelper public static String f