当前位置: 移动技术网 > 移动技术>移动开发>Android > Kotlin入门(26)数据库ManagedSQLiteOpenHelper

Kotlin入门(26)数据库ManagedSQLiteOpenHelper

2018年10月19日  | 移动技术网移动技术  | 我要评论

共享参数毕竟只能存储简单的键值对数据,如果需要存取更复杂的关系型数据,就要用到数据库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
    }

  

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网