你是否尝试过使用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", "[email protected]")
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 "[email protected]"
)或者在 database.use 中使用:
database.use {
insert("User",
"id" to 42, "name" to "John", "email" to "[email protected]"
}注意:在上面的例子中 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 where query |
whereArgs(String, args) | Specify a where query with arguments |
whereSimple(String, args) | Specify a where query 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 having expression |
having(String, args) | Specify a having expression 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"="" style="word-wrap: break-word; color: rgb(59, 67, 72); word-break: break-all;">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 已经提供了以下这些单列单行解析器:
ShortParserIntParserLongParserFloatParserDoubleParserStringParserBlobParser
你也可以在类的构造函数中创建一个行解析器。假设你有一个类:
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
}当 {} 块中没有抛出任何异常时,事务才会标记为成功。
共同學習,寫下你的評論
評論加載中...
作者其他優質文章