您的位置:首页 > 移动开发 > Android开发

Android客户端SQLite数据库升级方案

2015-12-04 14:26 489 查看
转载:http://lagunarock.iteye.com/blog/1729582

一,前言

没有采用Android自身提供的那一套数据库操作方式。而是想对SQLite数据库文件有更全面的控制,包括随时导出数据库文件修改表结构,增删数据等等。这样一来虽然在开放中得到不少便利,但是也带来了数据库升级的一些问题。

后来不得已采用了一种方案,可以解决问题,现将方案的全部实现细节记录下来。最后也会提出一些我认为有问题的地方。

二,数据库文件拷贝

程序不负责数据库的创建,SQLite数据库文件是在外部创建好的。程序启动阶段拷贝进SD卡。以达到对数据库结构的全面控制。

数据库文件存放位置见附件图片。

Java代码


public void copyDBFile() {

// 数据库路径

if (!FileOperator.checkFile(SysConst.DB_PATH)) {

boolean result = FileOperator.write2Sdcard(activity,

R.raw.scpip_collection, SysConst.DB_PATH);

Debug.log("无数据库文件,首次拷贝" + result);

if (!result) {

throw new IllegalAccessError(activity

.getString(R.string.copy_db_exception));

} else {

// 拷贝成功,更新数据库版本

try {

PackageInfo info = activity.getPackageManager()

.getPackageInfo(activity.getPackageName(), 0);

// 当前程序版本号,在AndroidManifest.xml中定义

int versionCode = info.versionCode;

Config.saveDbVer(versionCode);

Debug.log("拷贝成功" + result);

} catch (NameNotFoundException e) {

Debug.e(e);

}

}

} else {

// 数据库已存在的情况

if (dbUpdate.needUpdate() ) {

activity.showProgress("数据库升级中,请稍后", false);

new Thread() {

@Override

public void run() {

try {

Debug.log("update db");

dbUpdate.updateDb();

handler.sendEmptyMessage(0);

} catch (Exception e) {

Debug.e(e);

handler.sendEmptyMessage(-1);

}

};

}.start();

}

}

}

其中有几个要点:

1、检测数据库文件是否已经存在。不存在则从raw文件夹复制数据库文件拷贝至SD卡中指定目录。

2、数据库版本是根据应用的versionCode相同。拷贝数据库后,会把当前versionCode写入数据库的表中。

Xml代码


<manifest

xmlns:android="http://schemas.android.com/apk/res/android"

package="com.xxx"

android:versionCode="2"

android:versionName="1.01.001">

<uses-sdk android:minSdkVersion="8" />

versionCode在AndroidManifest.xml文件中。

在这种方案下,实际上是由versionCode控制数据库版本,versionName控制程序版本。

3、SD卡指定目录已经存在数据库文件的情况,则读取其中保存的数据库版本号,与versionCode对比,从而确定是否需要升级数据库。代码如下:

Java代码


public boolean needUpdate() {

int currVer = Config.getDbVer();

return currVer < getAppVersion();

}

public int getAppVersion(){

try {

PackageInfo info = context.getPackageManager().getPackageInfo(

context.getPackageName(), 0);

// 当前程序版本号,在AndroidManifest.xml中定义

return info.versionCode;

} catch (NameNotFoundException e) {

Debug.e(e);

return 1;

}

}

三,升级数据库

包括三个步骤:

1、从程序中拷贝新数据库文件至SD卡指定目录,命名为temp.db。

Java代码


String temp = SysConst.DB_FOLDER + "temp.db";

boolean s1 = FileOperator.write2Sdcard(context, R.raw.scpip_collection,temp);

2、分别获取两个数据源。

Java代码


//原数据库文件

BaseDao sd = new BaseDao();

//新数据库文件

BaseDao nd = new BaseDao(temp);

对于SQLite数据库来讲,数据源就是数据库文件。BaseDao是自己封装的,关键在于要可以配置不同的数据源

具体实现的相关代码如下:

Java代码


private String dbPath;

public BaseDao() {}

public BaseDao(String dbPath) {

this.dbPath = dbPath;

}

ublic SQLiteDatabase getDb() {

return SQLiteDatabase.openDatabase(SysConst.DB_PATH, null,

SQLiteDatabase.OPEN_READWRITE);

}

public SQLiteDatabase getDb(String dbPath) {

return SQLiteDatabase.openDatabase(dbPath, null,

SQLiteDatabase.OPEN_READWRITE);

}

这样就可以根据文件,获取不同的SQLiteDatabase 对象。

3、传输数据

把原数据库中的数据查询出来,插入到新数据库中。

Java代码


public <E>void transfer(BaseDao sd,BaseDao nd,Class<E> cls) throws Exception{

List<E> list = sd.find(cls, null);

nd.batchInsert(list);

}

这里有两个要点,

第一,使用了自行封装的ORM。

第二,使用了SQLite批量插入,增加写入效率。代码如下:

Java代码


@Override

public <T> void batchInsert(List<T> datas) throws Exception {

SQLiteDatabase dba = null;

if(dbPath == null){

dba = getDb();

} else {

dba = getDb(dbPath);

}

int size = datas.size();

try {

dba.beginTransaction();

for (int i = 0; i < size; i++) {

DaoHelper helper = new DaoHelper(datas.get(i));

String tableName = helper.getTableName();

String sql = "select 1 from " + tableName + " where "

+ helper.getPkCol() + "=";

Object id = helper.getPkValue();

if (id instanceof String) {

sql = sql + "'" + id + "'";

} else {

sql = sql + id;

}

c = dba.rawQuery(sql, null);

if (c != null ? (c.getCount() == 1) : false) {

c.close();

continue;

}

if(c != null){

c.close();

}

// SqlArgs sa = helper.prepareInsert();

// dba.execSQL(sa.getSql(), sa.getArgs());

dba.insert(helper.getTableName(), "", helper.getInsertContent());

}

dba.setTransactionSuccessful();

dba.endTransaction();

} finally {

dba.close();

}

}

4、写入当前数据库版本,即从程序获得的versionCode。

5、删除原数据库文件,重命名temp.db。

完整过程如下:

Java代码


public void updateDb() throws Exception {

//1,写入新的数据库文件,命名为temp

String temp = SysConst.DB_FOLDER + "temp.db";

boolean s1 = FileOperator.write2Sdcard(context, R.raw.scpip_collection,temp);

Debug.log("s1:" + s1);

if(s1) {

//原数据库文件

BaseDao sd = new BaseDao();

//新数据库文件

BaseDao nd = new BaseDao(temp);

//转移数据

//此处代码略

//删除原数据库文件,重命名临时数据库文件

if(FileOperator.delSdcardFile(SysConst.DB_PATH)){

File file = new File(temp);

file.renameTo(new File(SysConst.DB_PATH));

}

//此时更新数据库版本

Config.saveDbVer(getAppVersion());

}

}

至此,整个数据库升级完成。在保留原数据的基础上,获取了新的数据库结构。

四,问题

1,需要保留的原数据与新表结构不符。这个可以在程序中控制,创建新的OR映射。

2,效率问题,如果需要保留的数据量非常大的情况下,是否会出现问题。这个是亟待解决的,目前我还没有想到解决办法。

五,总结

这个方案针对的情况是外部创建数据库文件,程序启动时从apk包将数据库文件拷贝进SD卡,从而达到对数据库文件的完全控制。

方案步骤:

1,检测是否需要升级数据库文件。数据库文件版本是由versionCode控制。程序升级时,如果需要升级数据库,则要将versionCode+1。

2,将新数据库文件(存在于apk中),写入SD卡。

3,转移数据。

4,删除原数据库文件,重命名新数据库文件。

5,向新数据库写入当前数据库版本。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: