前言
Spring Data Jpa 框架的目标是显著减少实现各种持久性存储的数据访问层所需的样板代码量。Spring Data Jpa 存储库抽象中的中央接口是 Repository。它需要领域实体类以及领域实体 ID 类型作为类型参数来进行管理。该接口主要用作标记接口,以捕获要使用的类型并帮助您发现扩展该接口的接口。CrudRepository、JpaRepository 是更具体的数据操作抽象,一般我们在项目中使用的时候定义我们的领域接口然后继承 CrudRepository 或 JpaRepository 即可实现实现基础的 CURD 方法了,但是这种用法有局限性,不能处理超复杂的查询,而且稍微复杂的查询代码写起来也不是很优雅,所以下面看看怎么最优雅的解决这个问题。
扩展接口用法
/** * @author: kl @kailing.pub * @date: 2019/11/11 */ @Repository public interface SendLogRepository extends JpaRepository<SendLog,Integer> { /** * 派生的通过解析方法名称的查询 * @param templateName * @return */ List<SendLog> findSendLogByTemplateName(String templateName); /** * HQL * @param templateName * @return */ @Query(value ="select SendLog from SendLog s where s.templateName = :templateName") List<SendLog> findByTempLateName(String templateName); /** * 原生sql * @param templateName * @return */ @Query(value ="select s.* from sms_sendlog s where s.templateName = :templateName",nativeQuery = true) List<SendLog> findByTempLateNameNative(String templateName); }
优点:
- 1、这种扩展接口的方式是最常见的用法,继承 JpaRepository 接口后,立马拥有基础的 CURD 功能
- 2、还可以通过特定的方法名做解析查询,这个可以算 spring Data Jpa 的最特殊的特性了。而且主流的 IDE 对这种使用方式都有比较好的自动化支持,在输入要解析的方法名时会给出提示。
- 3、可以非常方便的以注解的形式支持 HQL 和原生 SQL
缺陷:
- 1、复杂的分页查询支持不好
缺陷就一条,这种扩展接口的方式要实现复杂的分页查询,有两种方式,而且这两种方式代码写起来都不怎么优雅,而且会把大量的条件拼接逻辑写在调用查询的 service 层。
- 第一种实例查询 (Example Query) 方式:public void testExampleQuery() { SendLog log = new SendLog(); log.setTemplateName("kl"); /* * 注意:withMatcher方法的propertyPath参数值填写领域对象的字段值,而不是实际的表字段 */ ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("templateName", match -> match.contains()); Example example = Example.of(log, matcher); Pageable pageable = PageRequest.of(0, 10); Page<SendLog> logPage = repository.findAll(example, pageable); }上面代码实现的语义是模糊查询 templateName 等于 "kl" 的记录并分页,乍一看这个代码还过得去哈,其实当查询的条件多一点,这种代码就会变得又臭又长,而且只支持基础的字符串类型的字段查询,如果查询条件有时间筛选的话就不支持了,在复杂点多表关联的话就更 GG 了,所以这种方式不合格直接上黑名单了。
- 第二种继承 JpaSpecificationExecutor 方式:
JPA 2 引入了一个标准 API,您可以使用它来以编程方式构建查询。Spring Data JPA 提供了使用 JPA 标准 API 定义此类规范的 API。这种方式首先需要继承 JpaSpecificationExecutor 接口,下面我们用这种方式实现和上面相同语义的查询:
public