结果方法

本章介绍查询终端方法和结果形态。

查询结果方法

select 和 join 查询使用同一组终端方法。根据需要接收的结果形态选择方法。

投影字段和 alias 见 📚投影。命名分页结果见 📚排序、分页与聚合。

toMapList 返回 Map 列表

需要把每行作为 Map<String, Any?> 接收时,使用 toMapList();它等价于 toList<Map<String, Any?>>()。

kotlin

val rows: List<Map<String, Any?>> = User()
    .select { [it.id, it.name] }
    .where { it.age >= 18 }
    .toMapList()

Mysql

SELECT `id`, `name`
FROM `user`
WHERE `user`.`age` >= :ageMin

返回结果是 Map 列表。

kotlin

listOf(
    mapOf("id" to 1, "name" to "Ada"),
    mapOf("id" to 2, "name" to "Grace")
)

toList 返回类型列表

当查询列可以映射到目标类型时,使用 toList()。

kotlin

val users: List<User> = User()
    .select()
    .where { it.age >= 18 }
    .toList()

Mysql

SELECT `id`, `name`, `age`
FROM `user`
WHERE `user`.`age` >= :ageMin

查询单列或自定义行类型时,显式传入泛型。

kotlin

val ids: List<Int> = User()
    .select { it.id }
    .toList<Int>()

val rows: List<UserSummary> = User()
    .select { [it.id, it.name] }
    .toList<UserSummary>()

生成投影也可以直接作为返回类型使用。

kotlin

val rows = User()
    .select { [it.id, f.length(it.name).alias("nameLength")] }
    .toList()

val firstLength = rows.first().nameLength

上面的无参调用返回编译器生成的投影类型。业务代码中需要命名 DTO 时,使用 select(UserSummary::class) { ... }。

dto

data class UserSummary(
    var id: Int? = null,
    var nameLength: Int? = null
) : KPojo

val summaries: List<UserSummary> = User()
    .select(UserSummary::class) {
        [it.id, f.length(it.name).alias("nameLength")]
    }
    .toList()

投影 alias 规则和生成结果形态见 📚投影。

toMap 返回一条 Map

期望返回一行 Map 时,使用 toMap(),也可以写成 first<Map<String, Any?>>()。Kronos 会为查询加上 LIMIT 1。

kotlin

val row: Map<String, Any?> = User()
    .select { [it.id, it.name] }
    .where { it.id == 1 }
    .toMap()

Mysql

SELECT `id`, `name`
FROM `user`
WHERE `user`.`id` = :id
LIMIT 1

toMap() 返回第一行 Map。

kotlin

mapOf("id" to 1, "name" to "Ada")

toMapOrNull 返回可空 Map

查询结果为空也属于预期情况时,使用 toMapOrNull(),也可以写成 firstOrNull<Map<String, Any?>>() 或 first<Map<String, Any?>?>()。一般情况下,firstOrNull<T>() 等价于 first<T?>()。

kotlin

val row: Map<String, Any?>? = User()
    .select { [it.id, it.name] }
    .where { it.id == 404 }
    .toMapOrNull()

kotlin

null

first 返回一条类型结果

期望返回一行并映射到目标类型时,使用 first()。Kronos 会为查询加上 LIMIT 1。

kotlin

val user: User = User()
    .select()
    .where { it.id == 1 }
    .first()

Mysql

SELECT `id`, `name`, `age`
FROM `user`
WHERE `user`.`id` = :id
LIMIT 1

查询单列或自定义行类型时,显式传入泛型。

kotlin

val name: String = User()
    .select { it.name }
    .where { it.id == 1 }
    .first<String>()

生成投影也可以用于单行结果方法。

kotlin

val row = User()
    .select { [it.id, it.name.alias("username")] }
    .where { it.id == 1 }
    .first()

val username = row.username

生成行可以像一个小结果对象一样使用,属性来自选中的字段和 alias。

firstOrNull 返回可空类型结果

查询结果为空时需要返回 null,使用 firstOrNull()。

kotlin

val user: User? = User()
    .select()
    .where { it.id == 404 }
    .firstOrNull()

kotlin

null

kotlin

data class UserRow(
    val id: Int?,
    val displayName: String?
)

val query = User()
    .select { [it.id, it.name.alias("displayName")] }
    .where { it.age >= 18 }

val users: List<UserRow> = query.toList<UserRow> { row ->
    UserRow(
        id = row.get<Int?>(1),
        displayName = row.get<String?>("displayName")
    )
}

val first: UserRow = query.first<UserRow> { row ->
    UserRow(row.get<Int?>("id"), row.get<String?>("displayName"))
}

val missing: UserRow? = query.firstOrNull<UserRow> { row ->
    UserRow(row.get<Int?>("id"), row.get<String?>("displayName"))
}

row.get<T>("label") 按列名或 alias 读取,row.get<T>(field) 使用 Field 的投影输出名,名称为空时回退到数据库列名,row.get<T>(position) 按 JDBC 列位置读取,位置从 1 开始;值按目标 KType 经过当前结果映射和 ValueCodec 转换。rowNumber 从 0 开始,row 在映射回调执行期间有效。KronosJdbcWrapper 支持该行映射能力。

在 join 后使用结果方法

Join 查询使用和 select 相同的结果方法。

kotlin

val rows: List<UserOrderRow> = User().join(Order()) { user, order ->
    leftJoin { user.id == order.userId }
        .select { [user.id, user.name, order.status] }
}.toList<UserOrderRow>()

Mysql

SELECT `user`.`id`, `user`.`name`, `order`.`status`
FROM `user`
LEFT JOIN `order`
ON `user`.`id` = `order`.`user_id`

在分页后使用结果方法

page(pageIndex, pageSize).toList() 直接返回当前页记录。需要总数时,在 page(...) 后调用 withTotal(),得到包含命名元数据的 PageResult<T>。

kotlin

val mapPage: PageResult<Map<String, Any?>> = User()
    .select { [it.id, it.name] }
    .page(1, 20)
    .withTotal()
    .toMapList()

val typedPage: PageResult<User> = User()
    .select()
    .page(1, 20)
    .withTotal()
    .toList()

val users = typedPage.records
val total = typedPage.total

游标执行返回 CursorResult<T>,包含 hasNext、nextCursor 和 records。

kotlin

val cursorPage: CursorResult<User> = User()
    .select()
    .orderBy { it.id.asc() }
    .cursor(pageSize = 20)
    .toList()

使用指定数据源

当本次查询需要使用指定数据源时,把 KronosDataSourceWrapper 传给任意终端方法。

kotlin

val customWrapper = CustomWrapper()

val users: List<User> = User()
    .select()
    .where { it.age >= 18 }
    .toList(customWrapper)

Wrapper 配置见 📚Kronos Data Source Wrapper。