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. CPU负载和CPU使用率

    参考CSDN博客:https://blog.csdn.net/ffzhihua/article/details/87257607 一.概念(本人理解) CPU负载:平均负载(load average) ...

  2. EffectiveJava-2

    一.使用类库 使用类库的好处: 无须关心方法是如何实现的,由算法专家花了大量时间设计.实现和测试这个方法,不仅保证了正确性,而且一旦有缺陷,下一个版本就会修复. 不必浪费时间为哪些与工作不太相关的问题 ...

  3. 微信小程序如何解析html内容

    最近项目上遇到在微信小程序里需要显示新闻内容,新闻内容是通过接口读取的服务器中的富文本内容,是html格式的,小程序默认是不支持html格式的内容显示的,那我们需要显示html内容的时候,就可以通过w ...

  4. Java描述设计模式(18):享元模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.使用场景 应用代码 public class C01_InScene { public static void main(String[] ...

  5. Java ->在mybatis和PostgreSQL Json字段作为查询条件的解决方案

    Date:2019-11-15 读前思考: 你没想到解决办法? PostgreSQL 数据库本身就支持还是另有解决办法? 说明:首先这次数据库使用到Json数据类型的原因,这次因为我们在做了一个app ...

  6. JS中的相等性判断===, ==, Object.is()

    首发地址:http://www.geeee.top/2019/11/15/equality-comparisons/,转载请注明出处 相信刚接触JS的人都会被他的想等性判断给整糊涂,看看下面代码,你能 ...

  7. Linux如何添加硬盘

    一.命令操作: df       #查看磁盘空间 fdisk     #分区/查看分区 mkfs      #格式化 df  -h(以人类易读) -m(以M为单位读取)            #查看硬 ...

  8. 还看不懂同事的代码?超强的 Stream 流操作姿势还不学习一下

    Java 8 新特性系列文章索引. Jdk14都要出了,还不能使用 Optional优雅的处理空指针? Jdk14 都要出了,Jdk8 的时间处理姿势还不了解一下? 还看不懂同事的代码?Lambda ...

  9. 多线程-等待(Wait)和通知(notify)

    1.为了支撑多线程之间的协作,JDK提供了两个非常重要的线程接口:等待wait()方法和通知notify()方法. 这两个方法并不是在Thread类中的,而是输出在Object类.这意味着任何对象都可 ...

  10. 微信web协议,群成员唯一uin,获取群成员唯一标识

    群成员唯一标识获取接口 全网最新,支持调试测试.觉得OK再付款! 800元出售源码 不讲价 联系QQ:2052404477