共享参数毕竟只能存储简单的键值对数据,如果需要存取更复杂的关系型数据,就要用到数据库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 = "ALTER TABLE $TABLE_NAME ADD COLUMN password VARCHAR;"
Log.d(TAG, "alter_sql:" + alter_sql)
db.execSQL(alter_sql)
}
} fun delete(condition: String): Int {
var count = 0
use {
count = delete(TABLE_NAME, condition, null)
}
return count
} fun insert(info: UserInfo): Long {
val infoArray = mutableListOf(info)
return insert(infoArray)
} fun insert(infoArray: MutableList<UserInfo>): Long {
var result: Long = -1
for (i in infoArray.indices) {
val info = infoArray[i]
var tempArray: List<UserInfo>
// 如果存在同名记录,则更新记录
// 注意条件语句的等号后面要用单引号括起来
if (info.name.isNotEmpty()) {
val condition = "name='${info.name}'"
tempArray = query(condition)
if (tempArray.size > 0) {
update(info, condition)
result = tempArray[0].rowid
continue
}
}
// 如果存在同样的手机号码,则更新记录
if (info.phone.isNotEmpty()) {
val condition = "phone='${info.phone}'"
tempArray = query(condition)
if (tempArray.size > 0) {
update(info, condition)
result = tempArray[0].rowid
continue
}
}
// 不存在唯一性重复的记录,则插入新记录
val cv = ContentValues()
cv.put("name", info.name)
cv.put("age", info.age)
cv.put("height", info.height)
cv.put("weight", info.weight)
cv.put("married", info.married)
cv.put("update_time", info.update_time)
cv.put("phone", info.phone)
cv.put("password", info.password)
use {
result = insert(TABLE_NAME, "", cv)
}
// 添加成功后返回行号,失败后返回-1
if (result == -1L) {
return result
}
}
return result
} @JvmOverloads
fun update(info: UserInfo, condition: String = "rowid=${info.rowid}"): Int {
val cv = ContentValues()
cv.put("name", info.name)
cv.put("age", info.age)
cv.put("height", info.height)
cv.put("weight", info.weight)
cv.put("married", info.married)
cv.put("update_time", info.update_time)
cv.put("phone", info.phone)
cv.put("password", info.password)
var count = 0
use {
count = update(TABLE_NAME, cv, condition, null)
}
return count
} fun query(condition: String): List<UserInfo> {
val sql = "select rowid,_id,name,age,height,weight,married,update_time,phone,password from $TABLE_NAME where $condition;"
Log.d(TAG, "query sql: " + sql)
var infoArray = mutableListOf<UserInfo>()
use {
val cursor = rawQuery(sql, null)
if (cursor.moveToFirst()) {
while (true) {
val info = UserInfo()
info.rowid = cursor.getLong(0)
info.xuhao = cursor.getInt(1)
info.name = cursor.getString(2)
info.age = cursor.getInt(3)
info.height = cursor.getLong(4)
info.weight = cursor.getFloat(5)
//SQLite没有布尔型,用0表示false,用1表示true
info.married = if (cursor.getInt(6) == 0) false else true
info.update_time = cursor.getString(7)
info.phone = cursor.getString(8)
info.password = cursor.getString(9)
infoArray.add(info)
if (cursor.isLast) {
break
}
cursor.moveToNext()
}
}
cursor.close()
}
return infoArray
} fun queryByPhone(phone: String): UserInfo {
val infoArray = query("phone='$phone'")
val info: UserInfo = if (infoArray.size>0) infoArray[0] else UserInfo()
return info
} fun deleteAll(): Int = delete("1=1") fun queryAll(): List<UserInfo> = query("1=1") }

因为ManagedSQLiteOpenHelper来自于Anko库,所以记得在UserDBHelper文件头部加上下面一行导入语句:

import org.jetbrains.anko.db.ManagedSQLiteOpenHelper

另外,有别于常见的anko-common包,Anko库把跟数据库有关的部分放到了anko-sqlite包中,故而还需修改模块的build.gradle文件,在dependencies节点中补充下述的anko-sqlite包编译配置:

    compile "org.jetbrains.anko:anko-sqlite:$anko_version"

现在有了用户信息表的管理类,在Activity代码中存取用户信息就方便多了,下面是往数据库存储用户信息和从数据库读取用户信息的代码片段:

    var helper: UserDBHelper = UserDBHelper.getInstance(this)
//往数据库存储用户信息
btn_save.setOnClickListener {
when (true) {
et_name.text.isEmpty() -> toast("请先填写姓名")
et_age.text.isEmpty() -> toast("请先填写年龄")
et_height.text.isEmpty() -> toast("请先填写身高")
et_weight.text.isEmpty() -> toast("请先填写体重")
else -> {
val info = UserInfo(name = et_name.text.toString(),
age = et_age.text.toString().toInt(),
height = et_height.text.toString().toLong(),
weight = et_weight.text.toString().toFloat(),
married = bMarried,
update_time = DateUtil.nowDateTime)
helper.insert(info)
toast("数据已写入SQLite数据库")
}
}
} //从数据库读取用户信息
private fun readSQLite() {
val userArray = helper.queryAll()
var desc = "数据库查询到${userArray.size}条记录,详情如下:"
for (i in userArray.indices) {
val item = userArray[i]
desc = "$desc\n第${i+1}条记录信息如下:" +
"\n 姓名为${item.name}" +
"\n 年龄为${item.age}" +
"\n 身高为${item.height}" +
"\n 体重为${item.weight}" +
"\n 婚否为${item.married}" +
"\n 更新时间为${item.update_time}"
}
if (userArray.isEmpty()) {
desc = "数据库查询到的记录为空"
}
tv_sqlite.text = desc
}

  

Kotlin入门(26)数据库ManagedSQLiteOpenHelper的更多相关文章

  1. Kotlin入门教程——目录索引

    Kotlin是谷歌官方认可的Android开发语言,Android Studio从3.0版本开始就内置了Kotlin,所以未来在App开发中Kotlin取代Java是大势所趋,就像当初Android ...

  2. 写给Android开发者的Kotlin入门

    写给Android开发者的Kotlin入门 转 https://www.jianshu.com/p/bb53cba6c8f4 Google在今年的IO大会上宣布,将Android开发的官方语言更换为K ...

  3. 【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  4. Kotlin入门第二课:集合操作

    测试项目Github地址: KotlinForJava 前文传送: Kotlin入门第一课:从对比Java开始 初次尝试用Kotlin实现Android项目 1. 介绍 作为Kotlin入门的第二课, ...

  5. Kotlin入门(32)网络接口访问

    手机上的资源毕竟有限,为了获取更丰富的信息,就得到辽阔的互联网大海上冲浪.对于App自身,也要经常与服务器交互,以便获取最新的数据显示到界面上.这个客户端与服务端之间的信息交互,基本使用HTTP协议进 ...

  6. Kotlin入门(28)Application单例化

    Application是Android的又一大组件,在App运行过程中,有且仅有一个Application对象贯穿应用的整个生命周期,所以适合在Application中保存应用运行时的全局变量.而开展 ...

  7. Kotlin入门(5)字符串及其格式化

    上一篇文章介绍了数组的声明和操作,包括字符串数组的用法.注意到Kotlin的字符串类也叫String,那么String在Java和Kotlin中的用法有哪些差异呢?这便是本文所要阐述的内容了. 首先要 ...

  8. Kotlin入门(9)函数的基本用法

    上一篇文章介绍了Kotlin新增的空安全机制,控制语句部分可算是讲完了,接下来将连续描述Kotlin如何定义和调用函数,本篇文章先介绍函数的基本用法. 前面几篇文章介绍控制语句之时,在setOnCli ...

  9. Kotlin入门(11)江湖绝技之特殊函数

    上一篇文章介绍了Kotlin对函数的输入参数所做的增强之处,其实函数这块Kotlin还有好些重大改进,集中体现在几类特殊函数,比如泛型函数.内联函数.扩展函数.尾递归函数.高阶函数等等,因此本篇文章就 ...

随机推荐

  1. json模块及其API

    模块:json 所包含API列表: json.dumps : 将python对象转换成json格式 json.loads : 将json格式字符串转换为python对象 ——————————————— ...

  2. mysql 开发进阶篇系列 5 SQL 优化(表优化)

    一. 使用sql提示 sql 提示(sql hint)是优化数据库的一个重要手段, 是在sql语句中加入一些人为的提示来达到优化操作的目的. 1.1 use index 在查询语句中表名的后面,添加u ...

  3. sql server 索引阐述系列二 索引存储结构

    一.概述. "流光容易把人抛,红了樱桃,绿了芭蕉“ 转眼又年中了,感叹生命的有限,知识的无限.在后续讨论索引之前,先来了解下索引和表数据的内部结构,这一节将介绍页的存储,页分配单元类型,区的 ...

  4. Android布局:宽度适应的横向跟随,防止挤掉重要视图

    不知道这样的布局该怎么描述,标题也是乱取的..直接上图吧 最近遇到了这样要求的布局: 1.上图中的“标题”长度不定,“状态”标签可能有多个并紧跟在标题右边,“属性”一直居右显示: 2.当“标题”过长, ...

  5. 转载:浏览器开发系列第一篇:如何获取最新chromium源码

    背景:     最近摊上一个事儿,领导非要让写一篇技术文章,思来想去,自己接触chrome浏览器时间也不短了,干脆就总结一下吧.于是乎,本文顺理成章.由于有些细节必需描述清楚,所以这次先讲如何拿到ch ...

  6. [java]创建一个默认TreeMap() key为什么不能为null

    本文版权归 远方的风lyh和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 先看一下 TreeMap 的 put(K key, V value) public TreeMap() ...

  7. React hooks实践

    前言 最近要对旧的项目进行重构,统一使用全新的react技术栈.同时,我们也决定尝试使用React hooks来进行开发,但是,由于React hooks崇尚的是使用(也只能使用)function c ...

  8. python实战学习之matplotlib绘图

    matplotlib 是最流行的Python底层绘图库,主要做数据可视化图表 可以将数据可视化,能够更直观的呈现数据 matplotlib绘图基本要点 首先实现一个简单的绘图 # 导入pyplot f ...

  9. python面向对象入门(1):从代码复用开始

    本文从代码复用的角度一步一步演示如何从python普通代码进化到面向对象,并通过代码去解释一些面向对象的理论.所以,本文前面的内容都是非面向对象的语法实现方式,只有在最结尾才给出了面向对象的简单语法介 ...

  10. Oracle 如何开启归档模式

    Oracle开启归档 场景:某所的数据库没有开启归档,如何开启归档模式的文档. 1.查看oracle归档状态 SQL> archive log list; 数据库日志模式 非存档模式 //目前不 ...