你是否尝试过使用Android的游标(Cursors)来解析SQLite的查询结果?你必须编写大量的样板代码,仅仅为了解析查询的结果行,并把它包含在数不清的try..finally中,适当的关闭所有打开的资源。
Anko提供了大量的扩展函数,来简化SQLite数据库的操作。
内容
在你的工程中使用Anko SQLite
添加 anko-sqlite 依赖至你的 build.gradle文件中:
    dependencies {        compile "org.jetbrains.anko:anko-sqlite:$anko\_version"
    }访问数据库
如果你使用 SQLiteOpenHelper,你一般会调用 getReadableDatabase() 或是 getWritableDatabase() (在生产代码中结果其实是一样的),但是你之后必须调用接收到的SQLiteDatabase对象的 close() 方法。你也需要在某些地方对帮助类进行缓存,并且如果你是在几个不同的线程中使用它,你必须进行并发处理。以上所用的东西都挺困难的。那也是为什么Android开发者不热衷与使用默认的SQLite API而倾向于使用像ORM(对象关系映射)这样的重量级包装。
Anko提供了一个特别的类 ManagedSQLiteOpenHelper 用来替换默认的哪一个。 它是这样子使用的:
    class MyDatabaseOpenHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDatabase", null, 1) {        companion object {            private var instance: MyDatabaseOpenHelper? = null
            @Synchronized
            fun getInstance(ctx: Context): MyDatabaseOpenHelper {                if (instance == null) {
                    instance = MyDatabaseOpenHelper(ctx.getApplicationContext())
                }                return instance!!
            }
        }        override fun onCreate(db: SQLiteDatabase) {            // Here you create tables
            db.createTable("Customer", ifNotExists = true, 
                        "id" to INTEGER + PRIMARY\_KEY + UNIQUE,                        "name" to TEXT,                        "photo" to BLOB)
        }        override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {            // Here you can upgrade tables, as usual
            db.dropTable("User", true)
        }
    }    // Access property for Context
    val Context.database: MyDatabaseOpenHelper        get() = MyDatabaseOpenHelper.getInstance(getApplicationContext())感觉怎么样? 不用在把你的代码放大 try 语句块中,现在你只需这么写:
    database.use {        // `this` is a SQLiteDatabase instance
    }数据库在执行 {} 中的所有代码后会被关闭。
异步调用的例子:
    class SomeActivity : Activity() {        private fun loadAsync() {
            async(UI) {                val result = bg { 
                    database.use { ... }
                }
                loadComplete(result)
            }
        }
    }之前提到的所有方法都可能抛出 SQLiteException。你必须自己去处理它,Anko没有理由假装那些错误不会发生。
创建和删除表
使用Anko你可以很方便的创建一个表,或是删除一个已经存在的表。语法很直接:
    database.use {
        createTable("Customer", true, 
            "id" to INTEGER + PRIMARY_KEY + UNIQUE,            "name" to TEXT,            "photo" to BLOB)
    }在SQLite中,有5个主要类型: NULL, INTEGER, REAL, TEXT and BLOB。但是每一行中可能有像 PRIMARY KEY 或是 UNIQUE这样的修饰符。 你可以使用加号添加到类型名的后面。
使用 dropTable 函数,删除一个表:
    dropTable("User", true)插入数据
同常情况下, 你需要一个 ContentValues 实例来往表中插入一行数据。这里有一个例子:
    val values = ContentValues()
    values.put("id", 5)
    values.put("name", "John Smith")
    values.put("email", "user@domain.org")
    db.insert("User", null, values)Anko让你直接将想插入的值作为 insert() 函数的参数:
    // Where db is an SQLiteDatabase
    // eg: val db = database.writeableDatabase
    db.insert("User", 
        "id" to 42,        "name" to "John",        "email" to "user@domain.org"
    )或者在 database.use 中使用:
    database.use {
        insert("User", 
            "id" to 42,            "name" to "John",            "email" to "user@domain.org"
    }注意:在上面的例子中 database 是一个 database helper 的实例,而 db 是一个 SQLiteDatabase 对象。
insertOrThrow(), replace(), replaceOrThrow() 等函数也存在并且有着相同的语义。
查询数据
Anko提供了一个很方便的查询建造器。它可以通过 db.select(tableName, vararg columns) 创建,其中 db 是 SQLiteDatabase的一个实例。
| 方法 | 描述 | 
|---|---|
| column(String) | Add a column to select query | 
| distinct(Boolean) | Distinct query | 
| whereArgs(String) | Specify raw String wherequery | 
| whereArgs(String, args) | Specify a wherequery with arguments | 
| whereSimple(String, args) | Specify a wherequery with?mark arguments | 
| orderBy(String, [ASC/DESC]) | Order by this column | 
| groupBy(String) | Group by this column | 
| limit(count: Int) | Limit query result row count | 
| limit(offset: Int, count: Int) | Limit query result row count with an offset | 
| having(String) | Specify raw havingexpression | 
| having(String, args) | Specify a havingexpression with arguments | 
加粗的函数使用一种特殊的方法来解析参数. 他们允许你以任意的顺序对参数进行赋值:
    db.select("User", "name")
        .whereArgs("(_id > {userId}) and (name = {userName})",            "userName" to "John",            "userId" to 42)在这里, {userId} 将会被 42 替换, {userName} 被 'John'替换。 如果类型不是数字类型 (Int, Float 等等。) 或 Boolean 型,值可能会被转义。 对于其他对一些类型,可能会使用到 toString()。
whereSimple 接受 String 类型到参数。 它工作起来和 SQLiteDatabase 中的 <a target="_blank title=" null"="" >query())一样 (问号标记 ? 会被参数中的真实值所替代)。
我们怎样执行查询操作?使用 exec() 函数。她接受一个 Cursor.() -> T类型的扩展函数。 它接收扩展函数然后它来关闭 Cursor ,你不要自己去做这件事:
    db.select("User", "email").exec {        // Doing some stuff with emails
    }解析查询结果
我们拥有一些 Cursor,我们怎么将它解析成一个标准的类呢? Anko提供了 parseSingle, parseOpt 和 parseList 函数,使得这件事做起来更加简单。
| 方法 | 描述 | 
|---|---|
| parseSingle(rowParser): T | Parse exactly one row | 
| parseOpt(rowParser): T? | Parse zero or one row | 
| parseList(rowParser): List<T> | Parse zero or more rows | 
注意: 如果接收到的 Cursor包含超过一行的数据, parseSingle() 和 parseOpt() 将会抛出异常。
现在的问题是: 什么是 rowParser?每个函数都支持两种不同类型的解析器: RowParser 和 MapRowParser:
    interface RowParser<T> {        fun parseRow(columns: Array<Any>): T
    }    interface MapRowParser<T> {        fun parseRow(columns: Map<String, Any>): T
    }如果你想让你的查询效率更高,RowParser (当你必须知道每一列的序号)。 parseRow 接受一个 Any 列表( Any 可以是除了 Long, Double, String 或是 ByteArray 之外的任何类型)。 MapRowParser,你可以使用字段的名称来获取它的值。
Anko 已经提供了以下这些单列单行解析器:
- ShortParser
- IntParser
- LongParser
- FloatParser
- DoubleParser
- StringParser
- BlobParser
你也可以在类的构造函数中创建一个行解析器。假设你有一个类:
class Person(val firstName: String, val lastName: String, val age: Int)
解析就像下面这么简单:
val rowParser = classParser<Person>()
目前为止,对于主构造函数中存在可选参数的类不支持创建解析器。注意,构造函数是通过反射进行调用的,对于很大的数据集,还是使用 RowParser 比较好。
如果你使用了 db.select() 建造器, 你可以直接在里面调用 parseSingle, parseOpt 和 parseList 并传入一个适当的解析器。
自定义行解析器
直接上实例,为 (Int, String, String) 这些列创建一个解析器。 最幼稚的做法是这样子:
    class MyRowParser : RowParser<Triple<Int, String, String>> {        override fun parseRow(columns: Array<Any>): Triple<Int, String, String> {            return Triple(columns[0] as Int, columns[1] as String, columns[2] as String)
        }
    }很好,现在在你的代码中有三个显式的转换。让我们使用rowParser函数来去除他们:
    val parser = rowParser { id: Int, name: String, email: String ->
        Triple(id, name, email)
    }就是这样子! rowParser 将所有转换都隐藏了起来,你可以按照自己的心情命名lambda表达式中的参数。
Cursor 流
Anko提供了一种函数式的方式访问 SQLite的 Cursor 。只要调用 cursor.asSequence() 或 cursor.asMapSequence() 扩展函数来获取数据行的序列。 不要忘记关闭 Cursor :)
更新值
为其中的一个user赋予一个新的名字:
    update("User", "name" to "Alice")
        .where("_id = {userId}", "userId" to 42)
        .exec()Update 也有一个 whereSimple() 方法,如果你是一个传统的人就使用它:
    update("User", "name" to "Alice")
        .`whereSimple`("_id = ?", 42)
        .exec()事务
有一个叫做 transaction() 的函数,允许你将几个数据库操作放到一个SQLite的事务中。
    transaction {        // Your transaction code
    }当 {} 块中没有抛出任何异常时,事务才会标记为成功。