共享参数毕竟只能存储简单的键值对数据,如果需要存取更复杂的关系型数据,就要用到数据库SQLite了。尽管SQLite只是手机上的轻量级数据库,但它麻雀虽小、五脏俱全,与Oracle一样存在数据库的创建、变更、删除、连接等DDL操作,以及数据表的增删改查等DML操作,因此开发者对SQLite的使用编码一点都不能含糊。当然,Android为了方便开发者的工作,已经提供了一个操作SQLite的工具类即SQLiteOpenHelper,在App开发时可由SQLiteOpenHelper派生出具体的业务表管理类。
但是,系统自带的SQLiteOpenHelper有个先天缺陷,就是它并未封装数据库管理类SQLiteDatabase,这造成一个后果:开发者需要在操作表之前中手工打开数据库连接,然后在操作结束后手工关闭数据库连接。可是手工开关数据库连接存在着诸多问题,比如数据库连接是否重复打开了?数据库连接是否忘记关闭了?在A处打开数据库却在B处关闭数据是否造成业务异常?以上的种种问题都制约了SQLiteOpenHelper的安全性。
有鉴于此,Kotlin结合Anko库推出了改良版的SQLite管理工具,名叫ManagedSQLiteOpenHelper,该工具封装了数据库连接的开关操作,使得开发者完全无需关心SQLiteDatabase在何时在何处调用,也就避免了手工开关数据库连接可能导致的各种异常。同时ManagedSQLiteOpenHelper的用法与SQLiteOpenHelper几乎一模一样,唯一的区别是:数据表的增删改查语句需要放在use语句块之中,具体格式如下:
use { //1、插入记录 //insert(...) //2、更新记录 //update(...) //3、删除记录 //delete(...) //4、查询记录 //query(...)或者rawQuery(...) }
其中表的查询操作还要借助于SQLite已有的游标类Cursor来实现,上述代码中的query和rawQuery方法,返回的都是Cursor对象,那么获取查询结果就得根据游标的指示一条一条遍历结果集合。下面是Cursor类的常用方法:
1、游标控制类方法,用于指定游标的状态:
close : 关闭游标
isClosed : 判断游标是否关闭
isFirst : 判断游标是否在开头
isLast : 判断游标是否在末尾
2、游标移动类方法,把游标移动到指定位置:
moveToFirst : 移动游标到开头
moveToLast : 移动游标到末尾
moveToNext : 移动游标到下一个
moveToPrevious : 移动游标到上一个
move : 往后移动游标若干偏移量
moveToPosition : 移动游标到指定位置
3、获取记录类方法,可获取记录的数量、类型以及取值。
getCount : 获取记录数
getInt : 获取指定字段的整型值
getFloat : 获取指定字段的浮点数值
getString : 获取指定字段的字符串值
getType : 获取指定字段的字段类型
接下来以用户注册信息数据库为例,看看Kotlin的数据库操作代码是怎样实现的,具体的实现代码示例如下:
class UserDBHelper(var context: Context, private var DB_VERSION: Int=CURRENT_VERSION) : ManagedSQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) { companion object { private val TAG = "UserDBHelper" var DB_NAME = "user.db" //数据库名称 var TABLE_NAME = "user_info" //表名称 var CURRENT_VERSION = 1 //当前的最新版本,如有表结构变更,该版本号要加一 private var instance: UserDBHelper? = null @Synchronized fun getInstance(ctx: Context, version: Int=0): UserDBHelper { if (instance == null) { //如果调用时没传版本号,就使用默认的最新版本号 instance = if (version>0) UserDBHelper(ctx.applicationContext, version) else UserDBHelper(ctx.applicationContext) } return instance!! } } override fun onCreate(db: SQLiteDatabase) { Log.d(TAG, "onCreate") val drop_sql = "DROP TABLE IF EXISTS $TABLE_NAME;" Log.d(TAG, "drop_sql:" + drop_sql) db.execSQL(drop_sql) val create_sql = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" + "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "name VARCHAR NOT NULL," + "age INTEGER NOT NULL," + "height LONG NOT NULL," + "weight FLOAT NOT NULL," + "married INTEGER NOT NULL," + "update_time VARCHAR NOT NULL" + //演示数据库升级时要先把下面这行注释 ",phone VARCHAR" + ",password VARCHAR" + ");" Log.d(TAG, "create_sql:" + create_sql) db.execSQL(create_sql) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { Log.d(TAG, "onUpgrade oldVersion=$oldVersion, newVersion=$newVersion") if (newVersion > 1) { //Android的ALTER命令不支持一次添加多列,只能分多次添加 var alter_sql = "ALTER TABLE $TABLE_NAME ADD COLUMN phone VARCHAR;" Log.d(TAG, "alter_sql:" + alter_sql) db.execSQL(alter_sql) alter_sql = &