Android-10 Kotlin封装Sqlite

很高兴,这个系列的文章终于上了两位数,给自己点个赞;不过上篇文章给自己挖了个坑,说是这次介绍anko中的sqlite
本来就是春节前的最后一周,完全无心看书啊,终于,终于,在参考了各种代码之后,算是做了一次初步的封装;

正如自己blog的描述,我们不生产代码,只是代码的搬运工,这次也一样,大部分的封装的代码参考自《Kotlin for Android Developers》,其中代码见传送门,只不过,我稍微简化修改了下,更适合自己的开发习惯,下面我们还是来看具体的代码:

step1

引入anko sqlite的库,这里我使用的版本是0.8.2,最新的是0.9.0,不过这次的封装不适用于最新版本,如果使用最新版本,请自行调整下:

添加以下代码到你的build.gradle文件中:

compile 'org.jetbrains.anko:anko-sqlite:0.8.2'

step2

创建表结构:

Tables.kt


package com.vslimit.kotlindemo.db

/**
 * Created by vslimit on 17/1/25.
 */
object PersonTable {
    val TABLE_NAME = "Person"
    val ID = "_id"
    val NAME = "name"
    val ADDRESS = "address"
    val COMPANY_ID = "companyId"
}

object CompanyTable{
    val TABLE_NAME = "Company"
    val ID = "_id"
    val NAME = "name"
    val ADDRESS = "address"
}

其实,最开始是打算,做个一对多的关系,后来,后来,因为时间不过,就做了CompanyTable的单表操作,有兴趣的可以把后续的做了。

step4

创建表对象:

DomainClasses.kt


package com.vslimit.kotlindemo.db

import java.util.*

/**
 * Created by vslimit on 17/1/26.
 */

data class Company(val map: MutableMap<String, Any?>) {
    var _id: Long by map
    var name: String by map
    var address: String by map

    constructor() : this(HashMap()) {
    }

    constructor(id:Long,name: String,address:String) : this(HashMap()) {
        this._id = id
        this.name = name
        this.address = address
    }

}

step4

访问数据库,这里引用官方教程的一段话:

如果你使用了SQLiteOpenHelper,你可以调用getReadableDatabase()或者getWritableDatabase()(在生产环境的代码中产生的结果是一样的),但是之后你必须在接收到SQLiteDatabase的中调用close() 方法。你也必须在某个地方缓存助手类,同时,如果你从多个线程中使用了它,还得注意并发访问的问题。所有的这些要点都是难度相当大的。这就是为什么Android开发者并没有真正的使用默认SQLite API而宁愿使用代价昂贵的包装器,比如ORM库的原因。

Anko提供了一个特殊的类ManagedSQLiteOpenHelper无缝的替代了默认的类。这里的实现是参照《Kotlin for Android Developers》,所以可能与官方实现有些区别:

package com.vslimit.kotlindemo.db

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import com.vslimit.kotlindemo.App
import org.jetbrains.anko.db.*

/**
 * Created by vslimit on 17/1/23.
 */
class DatabaseOpenHelper(ctx: Context = App.instance) : ManagedSQLiteOpenHelper(ctx, DB_NAME, null, DB_VERSION) {

    companion object {
        val DB_NAME = "person"
        val DB_VERSION = 1
        val instance by lazy { DatabaseOpenHelper() }
    }

    override fun onCreate(db: SQLiteDatabase) {
        db.createTable(CompanyTable.TABLE_NAME, true,
                CompanyTable.ID to INTEGER + PRIMARY_KEY + UNIQUE,
                CompanyTable.NAME to TEXT,
                CompanyTable.ADDRESS to TEXT)

//        db.createTable(PersonTable.TABLE_NAME,true,
//                PersonTable.ID to INTEGER + PRIMARY_KEY + UNIQUE,
//                PersonTable.NAME to TEXT,
//                PersonTable.ADDRESS to TEXT,
//                PersonTable.COMPANY_ID to INTEGER)
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        db.dropTable(CompanyTable.TABLE_NAME, true)
//        db.dropTable(PersonTable.TABLE_NAME, true)
        onCreate(db)
    }
}

ContextExtensions.kt扩展下:


val Context.database: DatabaseOpenHelper
    get() = DatabaseOpenHelper.instance
    

step5

对原有的Sqlite操作进行扩展:

DatabaseExtensions.kt


/*
 * Copyright 2015 Antonio Leiva
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.vslimit.kotlindemo.extensions

import android.database.sqlite.SQLiteDatabase
import org.jetbrains.anko.db.MapRowParser
import org.jetbrains.anko.db.SelectQueryBuilder

fun <T : Any> SelectQueryBuilder.parseList(parser: (Map<String, Any>) -> T): List<T> =
        parseList(object : MapRowParser<T> {
            override fun parseRow(columns: Map<String, Any>): T = parser(columns)
        })

fun <T : Any> SelectQueryBuilder.parseOpt(parser: (Map<String, Any>) -> T): T? =
        parseOpt(object : MapRowParser<T> {
            override fun parseRow(columns: Map<String, Any>): T = parser(columns)
        })

fun SQLiteDatabase.clear(tableName: String){
    execSQL("delete from $tableName")
}

fun SelectQueryBuilder.byId(id: Long) = whereSimple("_id = ?", id.toString())

大家看注释就晓得,这段代码是赤果果的照搬过来的,其实上面的代码也基本上是搬过来的,哈哈哈哈;

这里还需要有个Collections的扩展,我一起搬过来吧:

CollectionsExtensions.kt


/*
 * Copyright 2015 Antonio Leiva
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.vslimit.kotlindemo.extensions

import java.util.*

fun <K, V : Any> MutableMap<K, V?>.toVarargArray(): Array<out Pair<K, V>> =
        map({ Pair(it.key, it.value!!) }).toTypedArray()

inline fun <T, R : Any> Iterable<T>.firstResult(predicate: (T) -> R?): R {
        for (element in this) {
                val result = predicate(element)
                if (result != null) return result
        }
        throw NoSuchElementException("No element matching predicate was found.")
}

至此,搬代码结果,其实原作者里,还进行了进一步的封装,但是,就我个人而言,我觉得到此封装已经足够我们使用了,下面我们来看看如何使用:

新建一个fragment,一个textview,3个button,具体代码我就不写了,我只写fragmentsqlite的操作:


package com.vslimit.kotlindemo.fragment

import android.os.Bundle
import android.view.View
import com.vslimit.kotlindemo.R
import com.vslimit.kotlindemo.db.Company
import com.vslimit.kotlindemo.db.CompanyTable
import com.vslimit.kotlindemo.extensions.*
import kotlinx.android.synthetic.main.fragment_companys.*
import org.jetbrains.anko.async
import org.jetbrains.anko.db.insert
import org.jetbrains.anko.db.select
import org.jetbrains.anko.db.update
import org.jetbrains.anko.onClick
import java.util.*


/**
 * Created by vslimit on 16/12/31.
 */
class CompanyListFragment : BaseFragment() {
    override val layoutResourceId: Int = R.layout.fragment_companys

    companion object {
        fun getInstance(): CompanyListFragment {
            return CompanyListFragment()
        }
    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        init()
        showBtn.onClick {
            context.database.use {
                val company = select(CompanyTable.TABLE_NAME).byId(1).parseOpt { Company(HashMap(it)) }
                async() {
                    nameTv.text = company?.name
                }
            }
        }

        updateBtn.onClick {
            context.database.use {
                val company = select(CompanyTable.TABLE_NAME).byId(1).parseOpt { Company(HashMap(it)) }
                company?.name = "update_name"
                update(CompanyTable.TABLE_NAME, *company!!.map.toVarargArray()).where("_id = {id}", "id" to 1).exec()
            }
        }

        insertBtn.onClick {
            context.database.use {
                val company = Company()
                company.name = "demo_name"
                company.address = "demo_address"
                insert(CompanyTable.TABLE_NAME, *company.map.toVarargArray())
            }
        }
    }

    override fun onResume() {
        super.onResume()
    }

    fun init() {
        context.database.use {
            val list = select(CompanyTable.TABLE_NAME)
                    .parseList { Company(HashMap(it)) }
            async() {
                nameTv.text = list.size.toString()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
    }


}

fragment中,init()方法是查询所有数据,三个button分别是显示详情,插入和更新。

如果要操作事务

Anko中有一个叫做transaction()的特殊函数。它允许你将多个数据库操作放在一个闭合的单独的SQLite事务中。

transaction {
    // Your transaction code
}

如果大括号代码块中如果没有抛出任何的异常,事务将会被标记为成功!

备注: 如果你出于某些原因想中止事务,只需抛出TransactionAbortException异常。这种情况下,你不需要手动处理这些异常。

至此,基于Kotlin封装的的Sqlite已经实现,对于一些简单的sqlite操作,这样已经足够了,当然了,跟greendao相比,好处在于不用另外生成dao类的文件,便利性上,可能稍微差些,毕竟greendao是专职的orm;如果有更复杂的数据库操作,大家可以考虑Realm,而不是Sqlite

本文中的所有代码已经提交到git上了,大家如果喜欢就去git下star下吧。

(关于anko中的sqlite更多的内容,请查看官方文档:英文传送门中文传送门

下期预告:下期春节,就不预告了,上周的预告差点没完成,不过,还是要有个方向,下周可能是Realm,也可能是其他的,只能到时想到什么写什么了,在这里给各位朋友拜个早年,祝大家新年快乐,鸡年大吉!

Sqlite的代码详见:https://github.com/vslimit/kotlindemo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容