Spring Data JPA最为优秀的特性就是可以通过自定义方法名称生成查询来轻松创建查询SQL。Spring Data JPA提供了一个Repository编程模型,最简单的方式就是通过扩展JpaRepository,我们获得了一堆通用的CRUD方法,例如save,findAll,delete等。并且使用这些关键字可以构建很多的数据库单表查询接口:

public interface CustomerRepository extends JpaRepository<Customer, Long> {
Customer findByEmailAddress(String emailAddress);
List<Customer> findByLastname(String lastname, Sort sort);
Page<Customer> findByFirstname(String firstname, Pageable pageable);
}
  • findByEmailAddress生成的SQL是根据email_address字段查询Customer表的数据
  • findByLastname根据lastname字段查询Customer表的数据
  • findByFirstname根据firstname字段查询Customer表的数据

以上所有的查询都不用我们手写SQL,查询生成器自动帮我们工作,对于开发人员来说只需要记住一些关键字,如:findBy、delete等等。但是,有时我们需要创建复杂一点的查询,就无法利用查询生成器。可以使用本节介绍的Specification来完成。

笔者还是更愿意手写SQL来完成复杂查询,但是有的时候偶尔使用一下Specification来完成任务,也还是深得我心。不排斥、不盲从。没有最好的方法,只有最合适的方法!

一、使用Criteria API构建复杂的查询

是的,除了specification,我们还可以使用Criteria API构建复杂的查询,但是没有specification好用。我们来看一下需求:在客户生日当天,我们希望向所有长期客户(2年以上)发送优惠券。我们如何该检索Customer?

我们有两个谓词查询条件:

  • 生日
  • 长期客户-2年以上的客户。

下面是使用JPA 2.0 Criteria API的实现方式:

LocalDate today = new LocalDate();

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Customer> query = builder.createQuery(Customer.class);
Root<Customer> root = query.from(Customer.class); Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);
Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2); query.where(builder.and(hasBirthday, isLongTermCustomer));
em.createQuery(query.select(root)).getResultList();
  • 第一行LocalDate用于比较客户的生日和今天的日期。em是javax.persistence.EntityManager
  • 下三行包含用于查询Customer实体的JPA基础结构实例的样板代码。
  • 然后,在接下来的两行中,我们将构建谓词查询条件
  • 在最后两行中,where用于连接两个谓词查询条件,最后一个用于执行查询。

此代码的主要问题在于,谓词查询条件不易于重用,您需要先设置 CriteriaBuilder, CriteriaQuery,和Root。另外,代码的可读性也很差。

二、specification

为了能够定义可重用谓词条件,我们引入了Specification接口。

public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
}

结合Java 8的lambda表达式使用Specification接口时,代码变得非常简单

public CustomerSpecifications {
//查询条件:生日为今天
public static Specification<Customer> customerHasBirthday() {
return (root, query, cb) ->{
return cb.equal(root.get(Customer_.birthday), today);
};
}
//查询条件:客户创建日期在两年以前
public static Specification<Customer> isLongTermCustomer() {
return (root, query, cb) ->{
return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
};
}
}

现在可以通过CustomerRepository执行以下操作:

customerRepository.findAll(hasBirthday());
customerRepository.findAll(isLongTermCustomer());

我们创建了可以单独执行的可重用谓词查询条件,我们可以结合使用这些单独的谓词来满足我们的业务需求。我们可以使用 and(…)   和 or(…)连接specification。

customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));

与使用JPA Criteria API相比,它读起来很流利,提高了可读性并提供了更多的灵活性。

期待您的关注

使用Spring Data JPA的Specification构建数据库查询的更多相关文章

  1. SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法

    软件152 尹以操 首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml ...

  2. spring data jpa 使用方法命名规则查询

    按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写.框架在进行方法名解析时,会先把方法名多余的前缀 ...

  3. 【hql】spring data jpa中 @Query使用hql查询 问题

    spring data jpa中 @Query使用hql查询 问题 使用hql查询, 1.from后面跟的是实体类 不是数据表名 2.字段应该用实体类中的字段 而不是数据表中的属性 实体如下 hql使 ...

  4. spring data jpa封装specification实现简单风格的动态查询

    github:https://github.com/peterowang/spring-data-jpa-demo 单一实体的动态查询: @Servicepublic class AdvancedUs ...

  5. Spring Data JPA 复杂/多条件组合查询

    1: 编写DAO类或接口  dao类/接口 需继承 public interface JpaSpecificationExecutor<T> 接口: 如果需要分页,还可继承 public ...

  6. Spring Data JPA 实现多表关联查询

    本文地址:https://liuyanzhao.com/6978.html 最近抽出时间来做博客,数据库操作使用的是 JPA,相对比 Mybatis 而言,JPA 单表操作非常方便,增删改查都已经写好 ...

  7. JPA使用Specification构建动态查询

    封装Specification查询条件,在Spring Data JPA 2.0以前使用 Specifications 这个辅助类来操作where.not.and和or连接,在2.0版本以后这个类会被 ...

  8. spring data jpa 使用JPQL的方式查询

    用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询 @Que ...

  9. Spring Data JPA 的配置文件 已经数据库的状态

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

随机推荐

  1. CSPS模拟 87

    考场上思考量不可减少 否则分数秒变弟弟,考后秒变弱智 T1 二分答案.打的稍恶心 T2 线段树维护“如果我在这个点开枪,前方点的贡献有多大” 想明白了就很好理解了 另外已经飞过去八千里的鸟还输入进来干 ...

  2. AHOI2018 排列

    首先是那个非常吃shi的题意,想好久一会就能发现题里面的意思是: 如果某一个数的值为x,那么它必须排在第x个数后面. 然后我们就可以发现形成了一棵树,第i个数的父亲是i,如果出现了环就说明无解. 于是 ...

  3. CDQ分治学习笔记(三维偏序题解)

    首先肯定是要膜拜CDQ大佬的. 题目背景 这是一道模板题 可以使用bitset,CDQ分治,K-DTree等方式解决. 题目描述 有 nn 个元素,第 ii 个元素有 a_iai​.b_ibi​.c_ ...

  4. day2 上午 游戏 对应关系--->判断素数---->多重背包 神题

    #include<iostream> using namespace std; int n; ; ]; long long p[maxn]; long long dp[maxn][maxn ...

  5. Salesforce学习之路-developer篇(五)Aura组件原理及常用属性

    很喜欢曾经看到的一句话:以输出倒逼输入.以输出的形式强制自己学习,确实是高效的学习方式,真的很棒.以下仅为个人学习理解,如有错误,欢迎指出,共同学习. 1. 什么是Lightning Componen ...

  6. 基于c/s架构的远程登陆服务的步骤。

    1:上/下位机安装相应的服务程序.(确保内核支持该服务)2:上位机(作为服务器端)配置能够给下位机访问目录的所在地,及其读写权限.3:在/dev目录下创建该服务其所需要使用的虚拟文件设备,同时按照该服 ...

  7. python经典面试算法题1.2:如何从无序链表中移除重复项

    本题目摘自<Python程序员面试算法宝典>,我会每天做一道这本书上的题目,并分享出来,统一放在我博客内,收集在一个分类中. 1.2 如何实现链表的逆序 [蚂蚁金服面试题] 难度系数:⭐⭐ ...

  8. 一张图讲解最少机器搭建FastDFS高可用分布式集群安装说明

     很幸运参与零售云快消平台的公有云搭建及孵化项目.零售云快消平台源于零售云家电3C平台私有项目,是与公司业务强耦合的.为了适用于全场景全品类平台,集团要求项目平台化,我们抢先并承担了此任务.并由我来主 ...

  9. VS安装

    1. 只更改工作负载和单个组件 工作负载:我只勾选3个需要的 单个组件:  勾选  .NET 下Framework   别的不用改 2.点击安装,安装完成重启

  10. 领扣(LeetCode)最大连续1的个数 个人题解

    给定一个二进制数组, 计算其中最大连续1的个数. 示例 1: 输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3. 注意: 输入的数组 ...