前言

spring-data 项目中,为数据访问提供熟悉且一致的基于 Spring 的编程模型,同时仍保留底层数据存储的特殊特性,其中 spring-data-jpa 更容易实践领域驱动设计的特性,尤其收开发者欢迎。

然而,面对一些比较复杂的连表查询,如果严格按照 CQRS 的方案实践,方案又会显得比较笨重。在这个背景下, jpa 与 Mybatis 的混合方案,分别发挥了 jpa 模型的长处 与 Mybatis 动态 SQL 的长处,而规避了 jpa
动态 SQL 的短处与 Mybatis 模型的短处,受到了广泛的一致认可。

方案特点表现如下:

  • Jpa 做模型的持久操作,以及单表查询。
  • Mybatis 只做视图的复杂查询,并且不参与业务逻辑的运算(即代替 CQRS 查询层的逻辑)。

Mybatis PageHelper 的实践

spring-data 的中,提供了分页排序的 API 基本都是如下形状:

1
2
Iterable<T> findAll(Predicate predicate, Sort sort);
Page<T> findAll(Predicate predicate, Pageable pageable);

为了让 Mybatis 提供与 spring-data 一致的 API,我们希望使用如下:

1
2
3
4
5
6

@Transactional(readOnly = true)
public Page<OrderQueryMapper.OrderView>findAll(OrderQueryMapper.OrderParam orderParam, Pageable pageable){
return orderQueryMapper.findAll(orderParam, pageable);
}

于是,我们的 Mapper 查询层利用 page-helper 做了如下设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Mapper
public interface OrderQueryMapper {

List<OrderView> findAll(OrderParam orderParam);

default List<OrderView> findAll(OrderParam param, Sort sort) {
return QueryHelper.sort(this::findAll, param, sort);
}

default Page<OrderView> findAll(OrderParam param, Pageable pageable) {
return QueryHelper.page(this::findAll, param, pageable);
}

@Data
class OrderParam {
private String orderId;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

public abstract class QueryHelper {

/**
* @param mapperMethod 查询方法
* @param param 查询参数
* @param pageable 分页参数
* @param <RESULT> 结果类型
* @param <PARAM> 参数类型
* @return
*/
public static <RESULT, PARAM> Page<RESULT> page(Function<PARAM, List<RESULT>> mapperMethod, PARAM param, Pageable pageable) {
String orderBy = Optional.of(pageable.getSort())
.map(Iterable::spliterator)
.map(i -> StreamSupport.stream(i, false))
.orElseGet(Stream::empty)
.map(a -> a.getProperty() + " " + a.getDirection().name())
.collect(Collectors.joining(","));
// 注意 pageHelper 的 pageNum 从 1 开始
PageHelper.startPage(pageable.getPageNumber() + 1, pageable.getPageSize(), orderBy);
try {
com.github.pagehelper.Page<RESULT> result = (com.github.pagehelper.Page<RESULT>) mapperMethod.apply(param);
return new PageImpl<>(result.getResult(), pageable, result.getTotal());
} finally {
PageHelper.clearPage();
}
}

/**
* @param mapperMethod 查询方法
* @param param 查询参数
* @param sort 排序参数
* @param <RESULT> 结果类型
* @param <PARAM> 参数类型
* @return
*/
public static <RESULT, PARAM> List<RESULT> sort(Function<PARAM, List<RESULT>> mapperMethod, PARAM param, Sort sort) {
String orderBy = Optional.ofNullable(sort)
.map(Iterable::spliterator)
.map(i -> StreamSupport.stream(i, false))
.orElseGet(Stream::empty)
.map(a -> a.getProperty() + " " + a.getDirection().name())
.collect(Collectors.joining(","));
PageHelper.orderBy(orderBy);
try {
return mapperMethod.apply(param);
} finally {
PageHelper.clearPage();
}
}
}