软件迭代过程当中,业务不断更新,也要求软件持续更新。相应地,数据库更新升级也是不可避免的一个环节。Android做为客户端应用,数据库升级相对于服务端来讲会麻烦一些。常见的升级方式有:git
1.删除旧表和数据,建立新表。优势是简单方便,缺点是丢失了旧数据。适用于应用数据依赖度低的状况。github
2.在代码中兼容处理各版本数据库,建立新表,迁移旧数据到新表。优势是保留了旧数据,缺点是须要处理兼容个版本数据库差别,比较麻烦。若是经过代码来记录维护版本差别,会致使代码臃肿且极易出错。sql
本文介绍一种简单无缝的数据库升级方案,也是属于上述的第二种方式,可是简单、高效地处理了数据库版本兼容。代码已实如今DBFramework中,这是一个轻量的数据库框架,简单、规范、高效,可以很好的处理表之间的继承关系,能够为你节省不少开发工做。数据库
感兴趣的朋友能够了解一下。框架
数据库升级,实际上是对其中的表进行升级。要更新表而且保留数据,这就必需要知道新表和旧表之间的差别。若是咱们靠开发人员来记录各版本之间表的差别,无疑是一个困难和繁杂的工做。那么当咱们进行数据库的时候,客户端应用有没有办法知道这些差别,知道有哪些表须要更新呢?答案是确定的。在Sqlite中有一张叫作sqlite_master的系统表,其中记录了sqlite中的全部表信息,以下:ide
能够看到,其中除了表名,还有表建立语句。有了建立语句,咱们就能解析出表的字段信息,而后对比新表和旧表的字段,有差别则说明表须要更新,反之则不须要更新。以下代码用来获取现有的表信息:spa
/** * 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是一个记录表信息的类,拿到现有表信息,就能够和新的表信息进行对比,主要对比表字段的变化:code
public class Table { ... public boolean equalsColumns(Table table) { String myColumns = getColumnsStatement(CreateSql).toUpperCase(); String columns = getColumnsStatement(table.CreateSql).toUpperCase(); return (myColumns.equals(columns)); } ... }
若是无变化则跳过,有变化则更新,更新过程为:sqlite
1.将现有表以别名命名blog
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。
这种方案简单方便,并且几乎一劳永逸。推荐小伙伴们使用,若是有更好的方式或者宝贵意见,欢迎交流。