DDD死党:单引擎查询利器

基于索引的单表查询,是 MySQL 正确打开方式!
基于 QueryObject 的声明式查询,是简单查询的正确使用方式!
1、应用场景单表查询在业务开发中占比最大,是所有 CRUD Boy 的入门必备,所有人在 JAVABean 和 SQL 之间乐此不疲 。
整体架构如下图所示:

DDD死党:单引擎查询利器

文章插图
这是一个简单的分层架构 , 主要有:
  • 接入层:接收用户或其他服务的请求,对参数进行基本验证 。
  • 服务层:执行简单的业务逻辑,比如业务验证、数据转换、数据组装等 。
  • 数据访问层 。在 ORM 框架基础之上完成对数据库的访问 。
  • 数据库层 。负责数据存储和查询 。
其中 ORM 框架尤为重要,帮我们完成 对象 与 关系数据 间的相互转换 。因此,不少人认为玩好 ORM 就成为了高级开发人员 。而实际情况是:该部分是最枯燥、最没有技术含量的“技能” 。
目前 , 最常见的 ORM 便是 MyBatis 和 JPA , 以一个简单的分页查询 User 为例做一个简短介绍 。
按照用户状态分页查询 User 信息:
  • 用户状态和分页参数必填 。
  • 其他参数手机号、生日区间选填 。
查询入参如下:
@Datapublic class QueryUserByStatus {private Integer status;private String mobile;private Date birthAfter;private Date birthBefore;private Pageable pageable;}接口签名如下:
Page<User> queryByStatus(QueryUserByStatus queryByStatus);这个是最简单的 case,分别使用 MyBatis 和 Jpa 进行实现 。
(1)MyBatis
MyBatis是一款基于 Java 语言的持久层框架,它为SQL映射、数据处理和事务管理提供了优秀的支持 。MyBatis已成为使用最广泛的ORM框架之一,它支持极为灵活的自定义SQL , 同时也提供了与Spring Framework和Spring Boot等流行框架的集成方案,为Java程序员提供了极大的便利 。
基于MyBatis实现的核心代码如下:
@Autowiredprivate MyBatisUserMApper userMapper;public Page<MyBatisUser> queryByStatus(QueryUserByStatus query){// 状态不填if (query.getStatus()null){throw new IllegalArgumentException("status can not null");}// 分页必填if (query.getPageable()null){throw new IllegalArgumentException("pageable can not null");}MyBatisUserExample userExample = new MyBatisUserExample();MyBatisUserExample.Criteria criteria = userExample.createCriteria();// 添加状态过滤criteria.andStatusEqualTo(query.getStatus());// 添加手机号过滤if (query.getMobile() != null){criteria.andMobileEqualTo(query.getMobile());}// 添加生日过滤if (query.getBirthAfter() != null){criteria.andBirthAtGreaterThan(query.getBirthAfter());}// 添加生日过滤if (query.getBirthBefore() != null){criteria.andBirthAtLessThan(query.getBirthBefore());}// 添加分页信息userExample.setOffset(query.getPageable().offset());userExample.setRows(query.getPageable().getPageSize());// 查询数据long totalItems = this.userMapper.countByExample(userExample);List<MyBatisUser> users = this.userMapper.selectByExample(userExample);// 封装结果return new Page<>(users, query.getPageable(), totalItems);}(2)Jpa
JPA是Java Persistence API(Java持久化API)的简称,它是Sun官方提供的一套标准的ORM框架(对象关系映射框架) 。JPA提供了一种以面向对象方式来管理关系型数据库的方法,使开发人员可以使用对象而不是SQL来操作数据库 。JPA提供了一套公共的API,使开发人员可以在不同的ORM实现(如Hibernate、EclipseLink等)中自由切换 。
基于Jpa实现的核心代码如下:
@Autowiredprivate JpaUserRepository jpaUserRepository;public Page<JpaUser> queryByStatus(QueryUserByStatus queryByStatus){// 状态必填if (queryByStatus.getStatus()null){throw new IllegalArgumentException("status can not null");}// 分页必填if (queryByStatus.getPageable()null){throw new IllegalArgumentException("pageable can not null");}// 构建分页参数Pageable pageable = PageRequest.of(queryByStatus.getPageable().getPageNo(), queryByStatus.getPageable().getPageSize());// 构建过滤条件Specification<JpaUser> spec = Specification.where((root, query, cb) -> {List<Predicate> predicates = Lists.newArrayList();// 添加状态过滤Predicate statusPredicate = cb.equal(root.get("status"), queryByStatus.getStatus());predicates.add(statusPredicate);// 添加手机过滤if (queryByStatus.getMobile() != null){Predicate mobilePredicate = cb.equal(root.get("mobile") , queryByStatus.getMobile());predicates.add(mobilePredicate);}// 添加生日过滤if (queryByStatus.getBirthAfter() != null){Predicate birthAfterPredicate = cb.greaterThan(root.get("birthAt") , queryByStatus.getBirthAfter());predicates.add(birthAfterPredicate);}// 添加生日过滤if (queryByStatus.getBirthBefore() != null){Predicate birthBeforePredicate = cb.lessThan(root.get("birthAt") , queryByStatus.getBirthBefore());predicates.add(birthBeforePredicate);}// 组合过滤条件return cb.and(predicates.toArray(new Predicate[predicates.size()]));});// 查询数据org.springframework.data.domAIn.Page<JpaUser> all = this.jpaUserRepository.findAll(spec, pageable);// 封装结果return new Page<>(all.getContent(), queryByStatus.getPageable(), all.getTotalElements());}


推荐阅读