软件迭代过程中,业务不断更新,也要求软件持续更新。相应地,数据库更新升级也是不可避免的一个环节。Android作为客户端应用,数据库升级相对于服务端来说会麻烦一些。常见的升级方式有:

1.删除旧表和数据,创建新表。优点是简单方便,缺点是丢失了旧数据。适用于应用数据依赖度低的情况。

2.在代码中兼容处理各版本数据库,创建新表,迁移旧数据到新表。优点是保留了旧数据,缺点是需要处理兼容个版本数据库差异,比较麻烦。如果通过代码来记录维护版本差异,会导致代码臃肿且极易出错。

本文介绍一种简单无缝的数据库升级方案,也是属于上述的第二种方式,但是简单、高效地处理了数据库版本兼容。代码已实现在DBFramework中,这是一个轻量的数据库框架,简单、规范、高效,能够很好的处理表之间的继承关系,可以为你节省很多开发工作。

感兴趣的朋友可以了解一下。

数据库升级,其实是对其中的表进行升级。要更新表并且保留数据,这就必须要知道新表和旧表之间的差异。如果我们靠开发人员来记录各版本之间表的差异,无疑是一个困难和繁杂的工作。那么当我们进行数据库的时候,客户端应用有没有办法知道这些差异,知道有哪些表需要更新呢?答案是肯定的。在Sqlite中有一张叫做sqlite_master的系统表,其中记录了sqlite中的所有表信息,如下:

可以看到,其中除了表名,还有表创建语句。有了创建语句,我们就能解析出表的字段信息,然后对比新表和旧表的字段,有差异则说明表需要更新,反之则不需要更新。如下代码用来获取现有的表信息:

/**
* Get old tables.
* @return A map contains old tables which takes table name as key.
*/
public Map<String, Table> getOldTables() {
final String table = "sqlite_master";
final String[] columns = {"name", "sql"};
Cursor cursor = mDB.query(table, columns, "type='table'", null, null, null, null);
HashMap<String, Table> ret = null;
if (cursor.getCount() > 0) {
ret = new HashMap<String, Table>(cursor.getCount());
Table tmp;
while (cursor.moveToNext()) {
tmp = new Table(cursor.getString(0), cursor.getString(1));
ret.put(tmp.Name, tmp);
}
}
cursor.close();
return ret == null ? Collections.EMPTY_MAP : ret;
}

Table是一个记录表信息的类,拿到现有表信息,就可以和新的表信息进行对比,主要对比表字段的变化:

  public class Table {

   ...

  public boolean equalsColumns(Table table) {
String myColumns = getColumnsStatement(CreateSql).toUpperCase();
String columns = getColumnsStatement(table.CreateSql).toUpperCase();
return (myColumns.equals(columns));
} ... }

如果无变化则跳过,有变化则更新,更新过程为:

1.将现有表以别名命名

2.创建新表

3.迁移数据

4.删除现有表

如下代码所示:

 @Override
protected void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion, List<Table> tableList) {
Map<String, Table> oldMap = getOldTables();
Iterator<Table> iterator = tableList.iterator();
Table table;
Table oldTable;
String tempTable;
while (iterator.hasNext()) {
table = iterator.next();
if ((oldTable = oldMap.get(table.Name)) == null) {
//New table, create directly.
db.execSQL(table.CreateSql);
Log.i(TAG, "Table " + table.Name + " is a new table. Create it directly");
continue;
}
//Remove hit table.
oldMap.remove(table.Name);
if (oldTable.equalsColumns(table)) {
//Table not change.
Log.i(TAG, "Table " + table.Name + " doesn't need update");
continue;
}
tempTable = table.Name + TEMP_SUFFIX;
Log.i(TAG, "Update table: " + table.Name); //Table changed.
//Alter old table as temp.
alterTableName(oldTable.Name, tempTable);
//Create new table.
db.execSQL(table.CreateSql);
//Copy data.
copyData(getCommonColumn(oldTable.getColumns(), table.getColumns()), tempTable, table.Name);
//Delete old table
deleteTable(tempTable);
}
//Delete obsolete tables not hit.
deleteObsoleteTables(oldMap.values());
}

更多细节可查看源码:DBFramework

这种方案简单方便,而且几乎一劳永逸。推荐小伙伴们使用,如果有更好的方式或者宝贵意见,欢迎交流。

Android数据库无缝升级方案的更多相关文章

  1. Android 实现应用升级方案(暨第三方自动升级服务无法使用后的解决方案)

    第三方推送升级服务不再靠谱: 以前在做Android开发的时候,在应用升级方面都是使用的第三方推送升级服务,但是目前因为一些非技术性的问题,一些第三方厂商不再提供自动升级服务,比如友盟,那么当第三方推 ...

  2. Android数据库(sqlite)加密方案

    最近因为一些项目的安全性需要将数据库加密,一开始想到的就是先将数据库通过AES加密,然后运行时再解密,另一种是将数据库里的内容加密. 很快这两种方案都是不理想的,第一种加密方式形同虚设,第二种,如果加 ...

  3. android 数据库的升级与降级解决方案

    在我们写的程序中不避免的需要升级.或者说需要改变业务逻辑,那这个时候就需要修改数据库的字段,来适应不同的表结构 我们一般写数据库都是操作一个继承至 SQLiteOpenHelper 的类 然后我们需要 ...

  4. Android数据库升级

    随着Android应用版本的迭代,经常遇到数据库表结构发生改变,或者一些指定的表数据需要更新.这也就引出一个问题Android数据库的更新问题. Android数据库升级分类 Android数据库更新 ...

  5. 优雅的处理Android数据库升级的问题

    原始完成于:2015-04-27 19:28:22 提供一种思路,优雅的处理Android数据库升级的问题,直接上代码: 1 package com.example.databaseissuetest ...

  6. Android 数据库升级解决方案

    转自:http://blog.csdn.net/leehong2005/article/details/9128501 请考虑如下情况: 在数据库升级时,不同版本的数据库,他们定义的表结构完全可能是不 ...

  7. Android版本升级同时Sqlite数据库的升级及之前数据的保留

    http://www.cnblogs.com/wang340/archive/2013/05/06/3063135.html http://www.eoeandroid.com/forum.php?m ...

  8. Android(java)学习笔记192:SQLite数据库(表)的创建 以及 SQLite数据库的升级

    一.数据库的创建 1.文件的创建      //引用,如果文件不存在是不会创建的   File  file = new File("haha.txt"):     //输出流写数据 ...

  9. Android数据库升级、降级、创建(onCreate() onUpgrade() onDowngrade())[4]

    数据库版本升级对软件的管理操作. 我们手机经常会收到xxx软件升级什么的提醒,你的软件版本更新,同时你的数据库对应的版本也要相应的更新. 数据库版本更新需要主要的问题: 软件的1.0版本升级到1.1版 ...

随机推荐

  1. elementui+vue修改elementUi默认样式不生效

    重写,覆盖都不行, ! important  也不行. 原因是 在style标签加了 scoped 的原因.

  2. python-argparse批量修改后缀名-batch_file_rename.py

    #!/bin/usr/env python3 __author__ = 'nxz' import os import argparse def batch_rename(work_dir, old_e ...

  3. win10运行LoadRunner Controller遇到的坑

    第一次在win10上运行性能测试,结果 解决方法: 后来在同事的win10电脑上安装lr11后,竟然可以运行成功,哈哈,看到希望了,不用再安装回win7了,发现他的系统版本是win10企业版,而我的是 ...

  4. Spark SQL大数据处理并写入Elasticsearch

    SparkSQL(Spark用于处理结构化数据的模块) 通过SparkSQL导入的数据可以来自MySQL数据库.Json数据.Csv数据等,通过load这些数据可以对其做一系列计算 下面通过程序代码来 ...

  5. 网页布局之grid

    学习网格布局时,你可能会在网络上看到很多文章,内容不同,属性不同,真是让人摸不着头脑,到底哪个才是正确的?看了本篇文章,我想你会豁然开朗.比如,一会儿用grid-rows,一会儿用grid-defin ...

  6. window编程_消息分类

    Window应用程序利用Windows消息(Message)与应用程序及系统进行信息交换.消息分为:消息号:有事先定义好的消息名标识字节参数(wParam):用于提供消息的附加信息长字节参数(lPar ...

  7. Java中在实例化一个类时,这个类中没有初始值的int类型成员变量i,i的值是不是0?

    java中有两种类型一种是数值性,另一种是类变量数值性变量的初始值为0,类变量的初始化为null没做初始化成员变量int性变量是0, 在java中有这么一条规则,声明在方法中的变量在使用时必须要初始化 ...

  8. Scrapy基础(十四)————Scrapy实现知乎模拟登陆

    模拟登陆大体思路见此博文,本篇文章只是将登陆在scrapy中实现而已 之前介绍过通过requests的session 会话模拟登陆:必须是session,涉及到验证码和xsrf的写入cookie验证的 ...

  9. [LeetCode] Global and Local Inversions 全局与局部的倒置

    We have some permutation A of [0, 1, ..., N - 1], where N is the length of A. The number of (global) ...

  10. [LeetCode] Encode N-ary Tree to Binary Tree 将N叉树编码为二叉树

    Design an algorithm to encode an N-ary tree into a binary tree and decode the binary tree to get the ...