支付SDK中经常用到的Sqlite数据库
Posted Fastpay快付
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了支付SDK中经常用到的Sqlite数据库相关的知识,希望对你有一定的参考价值。
在Sdk和应用开发过程中,数据库只有Sqlite,其使用起来都是比较方便的(具有轻量级、基于文件、不需要安全以及跨平台的好处),本章节重点讲的不是Sqlite的使用,重点讲述在使用Sqlite数据库时候的存在的一些异常和修复方法以及推荐做法。
首先来看一段Sqlite的使用步骤:
1、创建数据库,包含创建Sqlite对象和表,升级情况下可以先Drop表,然后再创建,也可执行alter table等Sql语句。
/**
* 创建时传入上下文,数据库名称,版本等数据
*/
SQLiteOpenHelper dbHelper = new SQLiteOpenHelper(context, databaseName, null, dbVer) {
@Override
public void onCreate(SQLiteDatabase db) {
// 执行创建表的Sql语句
db.execSQL(String.format(CREATE_TABLE_SQL, mTableName));
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// db.execSQL("DROP TABLE IF EXISTS " + mTableName);
// 数据库升级
db.execSQL(String.format(MODIFY_TABLE_SQL, mTableName));
}
};
// 获取可写数据对象
mDatabase = dbHelper.getWritableDatabase();
2、数据操作包含insert,update,delete等,方式有两种,使用ContentValues和execSql
try{
ContentValues cvs = new ContentValues();
cvs.put(COLUMN_NAME_DATA, info.data);
cvs.put(COLUMN_NAME_PRIORITY, info.priority);
cvs.put(COLUMN_NAME_CREATE_TIME, info.createTime);
cvs.put(COLUMN_NAME_CITY, info.city);
cvs.put(COLUMN_NAME_REMARK, info.remark);
long result = mDatabase.insert(mTableName, null, cvs);
return result;
} catch (Exception e) {
// 处理异常
}
方法二:
//插入数据SQL语句
String sql="insert into city(data,priority,createtime,city,remark) values('GZ','1','2018-07-04 11:34:54', 'GuangZhou','广州又称羊城')";
//执行SQL语句
db.execSQL(sql);
游标的使用:
Cursor(游标)是SQLite 数据库查询返回的行数集合,Cursor是一个游标接口,提供了遍历查询结果的方法,如移动指针方法move(),获得列值方法getString()等。
如下代码:
SQLiteDatabase db = null;
Cursor cursor = null;
try {
// 获取可读数据库对象
db = getReadDB();
// 查询数据库字段
cursor = qb.query(db, COLUMNS, where, null, null, null, null);
// 循环游标
while (cursor.moveToNext()) {
// 取数据
tmpInfo.setCity(cursor.getString(cursor.getColumnIndex(Column.CITY)));
accountList.add(tmpInfo);
}
} catch (Exception e) {
} finally {
if (cursor != null) {
cursor.close();
cursor = null;
}
}
事务的使用:
Transaction(事务)是一个对数据执行工作单元,是以逻辑顺序完成的工作单位或序列。它具备ACID特性,什么叫ACID(原子性【Atomicity】、一致性【Consistency】、隔离性【Isolation】、持久性【Durability】)相信在大学学数据库原理知识的时候,应该对这个都有理解,这里就不细说了。
至此数据库的使用已经讲述完毕了,现在我们重点来讲述一下推荐做法和异常。
Tips1:
业务中出现多线程操作写入数据到数据库中时,需要使用事务,以免出现数据同步问题,无论多条一起变更还是一条(大量数据插入除外,大量数据循环插入容易出现notransaction is active异常)。伪代码如下:
beginTransaction();
for execSql->Sql
setTransactionSuccessful();
endTransaction(); // 放在finally语句中
Tips2:
为了性能及不出错习惯性我们都会打开,操作数据库,而不会显示的去关闭他。在项目中就遇到了这个坑,因为没关闭,造成应用退出前的最后一条数据没办法落盘的问题。
从原来的经验来讲,ContentProvider在数据库操作的时候会有回调通知以方便使用方做更新,如下伪代码:
count =db.delete(FrameStats.TABLE_NAME, selection, selectionArgs);
getContext().getContentResolver().notifyChange(Stats.CONTENT_URI, null);
Tips3:
执行SQL语句时,应尽量使用SQLiteDatabase类的insert()、update()、delete()方法,不要使用execSQL()方法,也不要直接执行字符串作为SQL语句,以免有SQL注入的风险,例如where条件中加入 and 1=1语句。
String sql =String.format("UPDATE %s SET data=%s WHERE id=%s", stat, data,"1 and 1=1");
db.execSQL(sql);
Tips4:
SQLite是一个文件,所有的数据都在一个db文件中。
支持数据库级并发,支持线程安全,即允许多个读事务同时运行,同一时刻最多只有一个写事务,读写冲突。
SQLite所有锁实现都是基于文件锁:
在事务开启时上锁,上锁和释放锁同样遵守文件锁协议,在事务提交或回滚时(以及close)才释放锁。
常见的异常和解决方案如下:
案例1:
Cursor window allocation of 2048 kb failed(游标内存泄漏异常)
解决方案:在使用数据库的时候忘记释放游标导致内存泄漏,请使用完毕之后手动关闭Cursor(cursor.close),一般推荐放在finally语句中。
案例2:
SqliteDiskIOException:disk I/O error
这种情况一般是由于多线程导致的,因为多线程写数据库,例如有些线程在修改数据,有些线程在删除数据,所以导致了这样的问题
解决方案:在多线程场景下写数据库,做好同步或者独立出一个写数据库的线程,其它线程需要写数据,就发消息给这个写线程。
案例3:
No such table android_metadata
在打开连接的数据时容易出现这个异常,原因是Meta表不存在,解决方案是在Open数据库的函数中加入SQLiteDatabase.NO_LOCALIZED_COLLATORS,同时也应该在OnDestory函数中关闭数据库。
SQLiteDatabase.openDatabase(stPathToDB,null, SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READONLY);
案例4:
WebViewDatabase$1.run(WebViewDatabase.java:1000)异常
在应用或Sdk有使用到WebView组件时,通常容易出现以下问题,因为网页数据缓存也是依赖数据库进行保存(所以在内部存储中可以见到webview.db 和webviewdb.db两个文件)
解决方案:setting.setCacheMode(WebSettings.LOAD_NO_CACHE);
deleteDatabase(“WebView.db”);和deleteDatabase(“WebViewCache.db”);
webView.clearHistory();
webView.clearFormData();
getCacheDir().delete(); 进行缓存数据库的清空。或者采用合适的缓存策略,例如:先判断是否有网络,如果有采用Load_default模式,如果没有Load_cache_else_network.
案例5:
Attempt to re-open an already-closedobject.
频繁操作数据库容易导致这个异常,通常是打开关闭重复等动作。
解决方案:在当前的业务流程中,一直打开数据库,不关闭,在退出的地方再统一对数据库进行关闭。
案例6:
Database is locked.
当我们在不同的线程中创建多个连接时,就会出现这个异常
解决方案:将数据库操作类做成一个单例,并且使用同步的关键字。
以上是关于支付SDK中经常用到的Sqlite数据库的主要内容,如果未能解决你的问题,请参考以下文章