Skip to content

Commit 6c074d9

Browse files
update entity sequence doc to use extension properties
1 parent 0da9ce2 commit 6c074d9

File tree

2 files changed

+40
-60
lines changed

2 files changed

+40
-60
lines changed

docs/source/en/entity-sequence.md

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@ In the previous section, we briefly learned how to obtain entity objects via seq
1010

1111
## Introduction
1212

13-
To create an entity sequence, we can use the extension function `sequenceOf`:
13+
To use sequence APIs, we need to create sequence objects first. In general, we’d like to define some extension properties for `Database`. These properties return new created sequence objects via `sequenceOf` and they can help us improve the readability of the code:
1414

1515
```kotlin
16-
val sequence = database.sequenceOf(Employees)
16+
val Database.departments get() = this.sequenceOf(Departments)
17+
val Database.employees get() = this.sequenceOf(Employees)
1718
```
1819

19-
Now we got a default sequence, which can obtain all employees from the table. Please know that Ktorm doesn't execute the query right now. The sequence provides an iterator of type `Iterator<Employee>`, only when we iterate the sequence using the iterator, the query is executed. The following code prints all employees using a for-each loop:
20+
The function `sequenceOf` returns default sequences, by which we can obtain all entity objects from the table. Please know that Ktorm doesn't execute the queries right now. The sequence provides an iterator of type `Iterator<E>`, only when we iterate the sequence using the iterator, the query is executed. The following code prints all employees using a for-each loop:
2021

2122
```kotlin
22-
for (employee in sequence) {
23+
for (employee in database.employees) {
2324
println(employee)
2425
}
2526
```
@@ -37,13 +38,13 @@ left join t_department _ref0 on t_employee.department_id = _ref0.id
3738
In addition to the for-each loop, we can also use the extension function `toList` to save all the items from the sequence into a list:
3839

3940
```kotlin
40-
val employees = sequence.toList()
41+
val employees = database.employees.toList()
4142
```
4243

4344
We can even add a filter condition by the `filter` function before calling `toList`:
4445

4546
```kotlin
46-
val employees = sequence.filter { it.departmentId eq 1 }.toList()
47+
val employees = database.employees.filter { it.departmentId eq 1 }.toList()
4748
```
4849

4950
Now the generated SQL is:
@@ -110,14 +111,13 @@ inline fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.filter(
110111
Similar to the `filter` function of `kotlin.sequences`, the `filter` function here also accepts a closure as its parameter, and the returned value from the closure will be used as a filter condition. Differently, our closure has a parameter of type `T`, the current table object, so what we get in the closure by `it` is the table object instead of an entity element. Besides, the closure's return type is `ColumnDeclaring<Boolean>` instead of `Boolean`. The following code obtains all the employees in department 1 by using `filter`:
111112

112113
```kotlin
113-
val employees = database.sequenceOf(Employees).filter { it.departmentId eq 1 }.toList()
114+
val employees = database.employees.filter { it.departmentId eq 1 }.toList()
114115
```
115116

116117
We can see that the usage is almost the same as `kotlin.sequences`, the only difference is the `==` in the lambda is replaced by the `eq` function. The `filter` function can also be called continuously, as all the filter conditions are combined with the `and` operator.
117118

118119
```kotlin
119-
val employees = database
120-
.sequenceOf(Employees)
120+
val employees = database.employees
121121
.filter { it.departmentId eq 1 }
122122
.filter { it.managerId.isNotNull() }
123123
.toList()
@@ -145,8 +145,7 @@ inline fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.filterColumns(
145145
By default, an entity sequence selects all the columns from the current table and referenced tables (if enabled), that may lead to unnecessary performance costs. If we are sensitive to the performance issue, we can use the `filterColumns` function, which supports us to custom the selected columns in the query. Assuming we want to get a list of departments, but their location data is not required, we can write codes like:
146146

147147
```kotlin
148-
val departments = database
149-
.sequenceOf(Departments)
148+
val departments = database.departments
150149
.filterColumns { it.columns - it.location }
151150
.toList()
152151
```
@@ -169,7 +168,7 @@ inline fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.sortedBy(
169168
Ktorm provides a `sortedBy` function, which allows us to specify the *order by* clause for the sequence's internal query. The function accepts a closure as its parameter in which we need to return a column or expression. The following code obtains all the employees and sorts them by their salaries:
170169

171170
```kotlin
172-
val employees = database.sequenceOf(Employees).sortedBy { it.salary }.toList()
171+
val employees = database.employees.sortedBy { it.salary }.toList()
173172
```
174173

175174
Generated SQL:
@@ -186,8 +185,7 @@ The `sortedBy` function sorts entities in ascending order, if we need descending
186185
Sometimes, we need to sort entities by two or more columns, then we can use the `sorted` function, which accepts a closure of type `(T) -> List<OrderByExpression>` as its parameter. The example below sorts the employees firstly by salaries descending, then by hire dates ascending:
187186

188187
```kotlin
189-
val employees = database
190-
.sequenceOf(Employees)
188+
val employees = database.employees
191189
.sorted { listOf(it.salary.desc(), it.hireDate.asc()) }
192190
.toList()
193191
```
@@ -211,7 +209,7 @@ fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.take(n: Int): EntitySequenc
211209
The `drop` and `take` functions are designed for pagination. The `drop` function returns a new sequence containing all elements except first n elements, while the `take` function returns a new sequence only containing first n elements. Usage example:
212210

213211
```kotlin
214-
val employees = database.sequenceOf(Employees).drop(1).take(1).toList()
212+
val employees = database.employees.drop(1).take(1).toList()
215213
```
216214

217215
If we are using MySQL, the generated SQL is:
@@ -238,7 +236,7 @@ fun <E : Any, C : MutableCollection<in E>> EntitySequence<E, *>.toCollection(des
238236
The `toCollection` function is used to collect all the elements in a sequence. It'll execute the internal query right now and iterate the results, adding them to the `destination`:
239237

240238
```kotlin
241-
val employees = database.sequenceOf(Employees).toCollection(ArrayList())
239+
val employees = database.employees.toCollection(ArrayList())
242240
```
243241

244242
In addition, Ktorm also provides some convenient `toXxx` functions based on `toCollection` to convert sequences to particular type of collections, they are `toList`, `toMutableList`, `toSet`, `toMutableSet`, `toHashSet`, `toSortedSet`.
@@ -257,7 +255,7 @@ The `map` function will execute the internal query and iterate the query results
257255
The following code obtains all the employees' names:
258256

259257
```kotlin
260-
val names = database.sequenceOf(Employees, withReferences = false).map { it.name }
258+
val names = database.employees.map { it.name }
261259
```
262260

263261
Generated SQL:
@@ -283,7 +281,7 @@ inline fun <E : Any, T : BaseTable<E>, C : Any> EntitySequence<E, T>.mapColumns(
283281
The `mapColumns` function is similar to `map`. Differently, its closure accepts the current table object `T` as the parameter, so what we get in the closure by `it` is the table object instead of an entity element. Besides, the closure's return type is `ColumnDeclaring<C>`, and we should return a column or expression needed to be selected from the database. Let's implement the same example as the previous one, the following code obtains all employees' names:
284282

285283
```kotlin
286-
val names = database.sequenceOf(Employees, withReferences = false).mapColumns { it.name }
284+
val names = database.employees.mapColumns { it.name }
287285
```
288286

289287
Now we can see there is only the required column in the generated SQL:
@@ -296,15 +294,7 @@ from t_employee
296294
If we want to select two or more columns, we can change to `mapColumns2` or `mapColumns3`, then we need to wrap our selected columns by `Pair` or `Triple` in the closure, and the function's return type becomes `List<Pair<C1?, C2?>>` or `List<Triple<C1?, C2?, C3?>>`. The example below prints the IDs, names and hired days of the employees in department 1:
297295

298296
```kotlin
299-
// MySQL datediff function
300-
fun dateDiff(left: LocalDate, right: ColumnDeclaring<LocalDate>) = FunctionExpression(
301-
functionName = "datediff",
302-
arguments = listOf(right.wrapArgument(left), right.asExpression()),
303-
sqlType = IntSqlType
304-
)
305-
306-
database
307-
.sequenceOf(Employees, withReferences = false)
297+
database.employees
308298
.filter { it.departmentId eq 1 }
309299
.mapColumns3 { Triple(it.id, it.name, dateDiff(LocalDate.now(), it.hireDate)) }
310300
.forEach { (id, name, days) ->
@@ -350,20 +340,20 @@ In addition to the basic forms, there are also many variants for these functions
350340
This serial of functions provide features of iteration and folding, and their usages are also the same as the corresponding ones of `kotlin.sequences`. The following code calculates the total salary of all employees:
351341

352342
```kotlin
353-
val totalSalary = database.sequenceOf(Employees).fold(0L) { acc, employee -> acc + employee.salary }
343+
val totalSalary = database.employees.fold(0L) { acc, employee -> acc + employee.salary }
354344
```
355345

356346
Of course, if only the total salary is needed, we don't have to write codes in that way. Because the performance is really poor, as all employees are obtained from the database. Here we just show you the usage of the `fold` function. It's better to use `sumBy`:
357347

358348
```kotlin
359-
val totalSalary = database.sequenceOf(Employees).sumBy { it.salary }
349+
val totalSalary = database.employees.sumBy { it.salary }
360350
```
361351

362352
### joinTo/joinToString
363353

364354
These two functions provide the feature of joining the sequence elements to strings, and their usages are also the same as the corresponding ones of `kotlin.sequences`. The following code joins all the employees' names to a string:
365355

366356
```kotlin
367-
val names = database.sequenceOf(Employees).joinToString(separator = ":") { it.name }
357+
val names = database.employees.joinToString(separator = ":") { it.name }
368358
```
369359

docs/source/zh-cn/entity-sequence.md

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@ related_path: en/entity-sequence.html
1010

1111
## 序列简介
1212

13-
要获取一个实体序列,我们可以使用 `sequenceOf` 扩展函数
13+
要使用序列 API,首先要创建实体序列的对象。一般来说,我们会给 `Database` 定义一些扩展属性,它们使用 `sequenceOf` 函数创建序列对象并返回。这些属性可以帮助我们提高代码的可读性
1414

1515
````kotlin
16-
val sequence = database.sequenceOf(Employees)
16+
val Database.departments get() = this.sequenceOf(Departments)
17+
val Database.employees get() = this.sequenceOf(Employees)
1718
````
1819

19-
这样我们就得到了一个默认的序列,它可以获得表中的所有员工。但是请放心,Ktorm 并不会马上执行查询,序列对象提供了一个迭代器 `Iterator<Employee>`,当我们使用它迭代序列中的数据时,查询才会执行。下面我们使用 for-each 循环打印出序列中所有的员工:
20+
`sequenceOf` 函数会返回一个默认的序列,它可以获得表中的所有实体对象。但是请放心,Ktorm 并不会马上执行查询,序列对象提供了一个迭代器 `Iterator<E>`,当我们使用它迭代序列中的数据时,查询才会执行。下面我们使用 for-each 循环打印出序列中所有的员工:
2021

2122
````kotlin
22-
for (employee in sequence) {
23+
for (employee in database.employees) {
2324
println(employee)
2425
}
2526
````
@@ -37,13 +38,13 @@ left join t_department _ref0 on t_employee.department_id = _ref0.id
3738
除了使用 for-each 循环外,我们还能用 `toList` 扩展函数将序列中的元素保存为一个列表:
3839

3940
````kotlin
40-
val employees = sequence.toList()
41+
val employees = database.employees.toList()
4142
````
4243

4344
我们还能在 `toList` 之前,使用 `filter` 扩展函数添加一个筛选条件:
4445

4546
```kotlin
46-
val employees = sequence.filter { it.departmentId eq 1 }.toList()
47+
val employees = database.employees.filter { it.departmentId eq 1 }.toList()
4748
```
4849

4950
此时生成的 SQL 会变成:
@@ -110,14 +111,13 @@ inline fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.filter(
110111
`kotlin.sequences``filter` 函数类似,`EntitySequence``filter` 函数也接受一个闭包作为参数,使用闭包中指定的筛选条件对序列进行过滤。不同的是,我们的闭包接受当前表对象 `T` 作为参数,因此我们在闭包中使用 `it` 访问到的并不是实体对象,而是表对象,另外,闭包的返回值也是 `ColumnDeclaring<Boolean>`,而不是 `Boolean`。下面使用 `filter` 获取部门 1 中的所有员工:
111112

112113
```kotlin
113-
val employees = database.sequenceOf(Employees).filter { it.departmentId eq 1 }.toList()
114+
val employees = database.employees.filter { it.departmentId eq 1 }.toList()
114115
```
115116

116117
可以看到,用法几乎与 `kotlin.sequences` 完全一样,不同的仅仅是在 lambda 表达式中的等号 `==` 被这里的 `eq` 函数代替了而已。`filter` 函数还可以连续使用,此时所有的筛选条件将使用 `and` 运算符进行连接,比如:
117118

118119
```kotlin
119-
val employees = database
120-
.sequenceOf(Employees)
120+
val employees = database.employees
121121
.filter { it.departmentId eq 1 }
122122
.filter { it.managerId.isNotNull() }
123123
.toList()
@@ -145,8 +145,7 @@ inline fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.filterColumns(
145145
实体序列默认会查询当前表对象和关联表对象(如果启用的话)中的的所有列,这有时会造成一定的性能损失,如果你对这些损失比较敏感的话,可以使用 `filterColumns` 函数。这个函数支持我们定制查询中的列,比如我们需要获取公司的部门列表,但是不需要部门的地址数据,代码可以这样写:
146146

147147
```kotlin
148-
val departments = database
149-
.sequenceOf(Departments)
148+
val departments = database.departments
150149
.filterColumns { it.columns - it.location }
151150
.toList()
152151
```
@@ -169,7 +168,7 @@ inline fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.sortedBy(
169168
`sortedBy` 函数用于指定查询结果的排序方式,我们在闭包中返回一个字段或一个表达式,然后 Ktorm 就会使用它对结果进行排序。下面的代码按工资从低到高对员工进行排序:
170169

171170
```kotlin
172-
val employees = database.sequenceOf(Employees).sortedBy { it.salary }.toList()
171+
val employees = database.employees.sortedBy { it.salary }.toList()
173172
```
174173

175174
生成 SQL:
@@ -186,8 +185,7 @@ order by t_employee.salary
186185
有时候,我们的排序需要考虑多个不同的字段,这时我们需要使用 `sorted` 方法,这个方法接受一个类型为 `(T) -> List<OrderByExpression>` 的闭包作为参数。下面是一个使用示例,它将员工按工资从高到低排序,在工资相等的情况下,再按入职时间从远到近排序:
187186

188187
```kotlin
189-
val employees = database
190-
.sequenceOf(Employees)
188+
val employees = database.employees
191189
.sorted { listOf(it.salary.desc(), it.hireDate.asc()) }
192190
.toList()
193191
```
@@ -211,7 +209,7 @@ fun <E : Any, T : BaseTable<E>> EntitySequence<E, T>.take(n: Int): EntitySequenc
211209
`drop``take` 函数用于实现分页的功能,`drop` 函数会丢弃序列中的前 n 个元素,`take` 函数会保留前 n 个元素丢弃后面的元素。下面是一个例子:
212210

213211
```kotlin
214-
val employees = database.sequenceOf(Employees).drop(1).take(1).toList()
212+
val employees = database.employees.drop(1).take(1).toList()
215213
```
216214

217215
如果我们使用 MySQL 数据库,会生成如下 SQL:
@@ -238,7 +236,7 @@ fun <E : Any, C : MutableCollection<in E>> EntitySequence<E, *>.toCollection(des
238236
`toCollection` 函数用于获取序列中的所有元素,它会马上执行查询,迭代查询结果中的元素,把它们添加到 `destination` 集合中:
239237

240238
````kotlin
241-
val employees = database.sequenceOf(Employees).toCollection(ArrayList())
239+
val employees = database.employees.toCollection(ArrayList())
242240
````
243241

244242
除此之外,Ktorm 还提供了一些简便的 `toXxx` 系列函数,用于将序列中的元素保存为特定类型的集合,它们分别是:`toList``toMutableList``toSet``toMutableSet``toHashSet``toSortedSet`
@@ -257,7 +255,7 @@ inline fun <E : Any, R> EntitySequence<E, *>.flatMap(transform: (E) -> Iterable<
257255
下面的代码可以获取所有员工的名字:
258256

259257
```kotlin
260-
val names = database.sequenceOf(Employees, withReferences = false).map { it.name }
258+
val names = database.employees.map { it.name }
261259
```
262260

263261
生成 SQL:
@@ -283,7 +281,7 @@ inline fun <E : Any, T : BaseTable<E>, C : Any> EntitySequence<E, T>.mapColumns(
283281
`mapColumns` 函数的功能与 `map` 类似,不同的是,它的闭包函数接受当前表对象 `T` 作为参数,因此我们在闭包中使用 `it` 访问到的并不是实体对象,而是表对象,另外,闭包的返回值也是 `ColumnDeclaring<C>`,我们需要在闭包中返回希望从数据库中查询的列或表达式。还是前面的例子,使用 `mapColumns` 获取所有员工的名字:
284282

285283
```kotlin
286-
val names = database.sequenceOf(Employees, withReferences = false).mapColumns { it.name }
284+
val names = database.employees.mapColumns { it.name }
287285
```
288286

289287
可以看到,这时生成的 SQL 中就只包含了我们需要的字段:
@@ -296,15 +294,7 @@ from t_employee
296294
如果你希望 `mapColumns` 能一次查询多个字段,可以改用 `mapColumns2``mapColumns3` 函数,这时我们需要在闭包中使用 `Pair``Triple` 包装我们的这些字段,函数的返回值也相应变成了 `List<Pair<C1?, C2?>>``List<Triple<C1?, C2?, C3?>>`。下面的例子会打印出部门 1 中所有员工的 ID,姓名和入职天数:
297295

298296
```kotlin
299-
// MySQL datediff function
300-
fun dateDiff(left: LocalDate, right: ColumnDeclaring<LocalDate>) = FunctionExpression(
301-
functionName = "datediff",
302-
arguments = listOf(right.wrapArgument(left), right.asExpression()),
303-
sqlType = IntSqlType
304-
)
305-
306-
database
307-
.sequenceOf(Employees, withReferences = false)
297+
database.employees
308298
.filter { it.departmentId eq 1 }
309299
.mapColumns3 { Triple(it.id, it.name, dateDiff(LocalDate.now(), it.hireDate)) }
310300
.forEach { (id, name, days) ->
@@ -350,13 +340,13 @@ where t_employee.department_id = ?
350340
这一系列函数及其变体为序列提供了迭代、折叠等功能,它们的用法也与 `kotlin.sequences` 的同名函数一模一样,具体可以参考 Kotlin 标准库的相关文档。下面使用 `fold` 计算所有员工的工资总和:
351341

352342
```kotlin
353-
val totalSalary = database.sequenceOf(Employees).fold(0L) { acc, employee -> acc + employee.salary }
343+
val totalSalary = database.employees.fold(0L) { acc, employee -> acc + employee.salary }
354344
```
355345

356346
当然,如果仅仅为了获得工资总和,我们没必要这样做。这是性能低下的写法,它会查询出所有员工的数据,然后对它们进行迭代,这里仅用作示范,更好的写法是使用 `sumBy` 函数:
357347

358348
```kotlin
359-
val totalSalary = database.sequenceOf(Employees).sumBy { it.salary }
349+
val totalSalary = database.employees.sumBy { it.salary }
360350
```
361351

362352
### joinTo/joinToString
@@ -366,6 +356,6 @@ val totalSalary = database.sequenceOf(Employees).sumBy { it.salary }
366356
下面使用 `joinToString` 把所有员工的名字拼成一个字符串:
367357

368358
```kotlin
369-
val names = database.sequenceOf(Employees).joinToString(separator = ":") { it.name }
359+
val names = database.employees.joinToString(separator = ":") { it.name }
370360
```
371361

0 commit comments

Comments
 (0)