注解配置于 QueryObject 之上,以声明化的方式,对过滤功能进行描述 。常见注解如下:
含义
FieldEqualTo
等于
FieldGreaterThan
大于
FieldGreaterThanOrEqualTo
大于等于
FieldIn
in 操作
FieldIsNull
是否为 null
FieldLessThan
小于
FieldLessThanOrEqualTo
小于等于
FieldNotEqualTo
不等于
FieldNotIn
not in
EmbeddedFilter
嵌入查询对象
针对之前的 User 查询实例,对应的 查询对象定义如下:
@Datapublic class QueryUserByStatus {// 状态相等@FieldEqualTo("status")@NotNullprivate Integer status;// 手机号相等@FieldEqualTo("mobile")private String mobile;// 生日比该值大@FieldGreaterThan("birthAt")private Date birthAfter;// 生日比该值小@FieldLessThan("birthAt")private Date birthBefore;// 自动具备分页能力private Pageable pageable;}
接口有了 QueryObject 之后,需要一组查询 API 以满足各个场景需求,标准的 API 接口定义如下:public interface QueryObjectRepository<E> {// 检查查询对象的有效性void checkForQueryObject(Class cls);// 单条查询<Q> E get(Q query);// 分页查询default <Q, R> R get(Q query, Function<E, R> converter) {E entity = this.get(query);return entitynull ? null : converter.apply(entity);}// 统计查询<Q> Long countOf(Q query);// 列表查询default <Q, R> List<R> listOf(Q query, Function<E, R> converter) {List<E> entities = this.listOf(query);return CollectionUtils.isEmpty(entities) ? Collections.emptyList() : (List)entities.stream().filter(Objects::nonNull).map(converter).filter(Objects::nonNull).collect(Collectors.toList());}// 列表查询<Q> List<E> listOf(Q query);// 分页查询default <Q, R> Page<R> pageOf(Q query, Function<E, R> converter) {Page<E> entityPage = this.pageOf(query);return entityPagenull ? null : entityPage.convert(converter);}// 分页查询<Q> Page<E> pageOf(Q query);}
集成示例有了 QueryObject 和 API 之后,便可以轻松完成各种查询:public class SingleQueryService {@Autowiredprivate QueryObjectRepository<JpaUser> repository;public List<JpaUser> listByStatus(QueryUserByStatus query){return repository.listOf(query);}public Long countByStatus(QueryUserByStatus query){return this.repository.countOf(query);}public Page<JpaUser> pageByStatus(QueryUserByStatus query){return this.repository.pageOf(query);}}
万事具备,只欠最后的 QueryObjectRepository 实现,针对不同的 ORM 提供不同的实现 。(2)MyBatis 支持
基于 MyBatis Generator 的 Example 机制实现,需要配置相关的 Generator 以生成 EntityExample 对象 。直接继承BaseReflectBasedExampleSingleQueryRepository,注入 Mapper 实现,指定好 Example 类即可,具体如下:
@Servicepublic class MyBatisBasedQueryRepository extends BaseReflectBasedExampleSingleQueryRepository {// 注入 MyBatis 的 Mapper 类public MyBatisBasedQueryRepository(MyBatisUserMapper mapper) {// 指定查询所需的 Example 类super(mapper, MyBatisUserExample.class);}}
整体架构如下:文章插图
核心流程如下:
- ExampleConverter 将输入的 QueryObject 转换为 XXXExample 实例 。
- 使用 XXXExample 实例 调用 XXXMapper 的 selectByExample 方法获取返回值 。
- 返回值通过 ResultConverter 将 Entity 转换为最终结果 。
- Pageable 。从 QueryObject 中读取 Pageable 属性 , 并设置 Example 对象的 offset 和 rows 属性 。
- Sort 。从 QueryObject 中读取 Sort 属性,并设置 Example 对象的 orderByClause 属性 。
- 过滤注解 。遍历 QueryObject 中属性,根据注解查找到注解处理器,由注解处理器为 Example 添加 Criteria,以进行数据过滤 。
基于 JPA 框架的 JpaSpecificationExecutor 实现,EntityRepository 需继承 JpaSpecificationExecutor 接口 。
推荐阅读
- DDD死党:内存Join——将复用和扩展用到极致
- DDD 必备架构--六边形架构
- 解密DDD:高内聚对象组的维护之道
- 什么是基友什么是死党 什么是基友
- 死党是什么关系 基友是什么关系
- 死党是和闺蜜的意思一样吗知乎 死党是和闺蜜的意思一样吗
- DDD实战 - Repository模式的妙用
- 为什么从 MVC 到 DDD,架构的本质是什么?
- DDD 中关于应用架构的那些事
- 基于DDD的微服务落地