跳到主要内容

动态排序

静态排序

首先,让我们来看看静态排序的用法,认识Jimmer的排序的概念

public List<Book> findBooks() {

BookTable table = Tables.BOOK_TABLE;

return sqlClient
.createQuery(table)
.orderBy(table.name())
.orderBy(table.edition().desc())
.orderBy(table.score().desc().nullsLast())
.select(table)
.execute();
}
警告

nullsFirst/nullsLast需要数据库支持,比如,Oracle。

对于更多不支持此功能的数据库,请使用常见表达式中的case表达式。

动态排序

动态排序有两种用法

  • orderByIf
  • 客户端指定的排序

orderByIf

orderByIf的用法和whereIf的用法类似。

信息
  • 和where不同,多个orderBy对先后顺序非常敏感,因此orderByIf并不如whereIf那样实用。

    尽管如此,Jimmer仍然支持orderByIf,毕竟这是一种最简单最基础的用法。

  • orderByIf其实是动态排序的Java DSL的API,Java DSL使用链式编程风格,为了不打断链式代码的书写,提供orderByIf

    Kotlin DSL使用lambda书写查询,本身就可以混入任意复杂的逻辑,所以,kotlin无需提供orderByIf

假设OrderMode是一个枚举,具有取值NAMEPRICE,则可以按照下面的例子使用orderByIf

public List<Book> findBooks(OrderMode orderMode) {

BookTable table = Tables.BOOK_TABLE;

return sqlClient
.createQuery(table)
.orderByIf(mode == OrderMode.NAME, table.name())
.orderByIf(mode == OrderMode.PRICE, table.price())
.select(table)
.execute();
}

客户端指定排序

很多时候,前端UI允许用户通过操作表格组件来实现动态排序。即,排序的决定权在于客户端,服务端被动地接受参数,按客户端的排序要求执行查询。

客户端通过传递字符串参数可以指定动态排序,可以通过函数makeOrders把字符串转化为Jimmer排序需要的List<Order>

makeOrders定义如下

public class Order {

public static List<Order> makeOrders(Props table, String ... codes) {
...省略实现...
}

...省略其他代码...
}

其中,第一个参数table为SQL DSL中的主表。

makeOrders的使用方式非常灵活,例如

  • 用多个参数实现多列排序

    Order.makeOrders(table, "name", "edition desc")
  • 可以把多个参数合并成一个,并在字符串内部用,;做分割

    Order.makeOrders(table, "name, edition desc")
    信息

    实际项目中,大部分情况下都属于这种单参用法,因为只提供一个HTTP参数是最简单的。

  • 甚至支持按照引用关联 (一对一、多对一) 进行连接

    Order.makeOrders(table, "store.city.name; store.name; name")
    信息

    动态连接中所有特性对这种隐含连接有效

Order.makeOrders的使用方式如下

public List<Book> findBooks(String sort) {

BookTable table = Tables.BOOK_TABLE;

return sqlClient
.createQuery(table)
.orderBy(Order.makeOrders(table, sort))
.select(table)
.execute();
}

如果调用findBooks("store.name asc, name asc"),则生成如下SQL

select
tb_1_.ID,
tb_1_.NAME,
tb_1_.EDITION,
tb_1_.PRICE,
tb_1_.STORE_ID
from BOOK tb_1_
/* highlight-next-line */
inner join BOOK_STORE tb_2_
on tb_1_.STORE_ID = tb_2_.ID
order by
tb_2_.NAME asc,
tb_1_.NAME desc

由此可见,如果排序中包含隐含连接,Jimmer一样会构建动态连接。

处理SpringData的Sort

在上面的例子中,我们直接把客户端传递的排序字符串转化为Jimmer SQL AST的排序。

然而,和SpingData协作时,可能需要处理org.springframework.data.domain.Sort类型。

我们可以如此编写查询。这次,参数不再是字符串,而是Spring Data的Sort类。

public List<Book> findBooks(Sort sort) {

BookTable table = Tables.BOOK_TABLE;

return sqlClient
.createQuery(table)
.orderBy(table, SpringOrders.toOrders(sort))
.select(table)
.execute();
}

这段代码解释如下:

  • Java:

    Jimmer的Java API提供了一个工具类,org.babyfish.jimmer.spring.repository.SpringOrders,其静态方法toOrders把spring-data的Sort对象转化成Jimmer SQL DSL中的Order对象数组。

    SpringOrders.toOrders具备两个参数

    • table: SQL DSL中的主表

    • sort:外部传递的spring-data之Sort对象

    SpringOrders.toOrders把Spring Data的Sort对象转化为Jimmer SQL DSL的Order对象数组后,就可以用Jimmer查询对象的orderBy实现排序。

  • Kotlin

    Jimmer的Kotlin API扩展了Jimmer查询对象,可以直接按照Spring Data的Sort对象排序。

至此,我们已经示范了如何把Spring Data的Sort对象转化为Jimmer中的排序操作。

为了进一步简化用户代码,Jimmer还提供了工具类org.babyfish.jimmer.spring.model.SortUtils, 其静态方法toSort可以把客户端传递的排序字符串转化为spring-data的Sort对象。比如

Sort sort = SortUtils.toSort("name asc, edition desc");

+------------------------+
| 客户端构建的动态排序字符串 |
+-----------+------------+
|
SortUtils.toSort
|
\|/
+------------------------+
| Spring-Data的Sort对象 |
+-----------+------------+
|
Java: 先调SpringOrders.toOrders,再orderBy
Ktlin: 直接用sort对象进行orderBy操作
|
\|/
+------------------------+
| Jimmer查询AST中的排序 |
+-----------+------------+