使用Spring Data JPA的Specification构建数据库查询

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相比,它读起来很流利,提高了可读性并提供了更多的灵活性。
期待您的关注
- 向您推荐博主的系列文档:《手摸手教您学习SpringBoot系列-16章97节》
- 本文转载注明出处(必须带连接,不能只转文字):字母哥博客。
使用Spring Data JPA的Specification构建数据库查询的更多相关文章
- SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法
软件152 尹以操 首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml ...
- spring data jpa 使用方法命名规则查询
按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写.框架在进行方法名解析时,会先把方法名多余的前缀 ...
- 【hql】spring data jpa中 @Query使用hql查询 问题
spring data jpa中 @Query使用hql查询 问题 使用hql查询, 1.from后面跟的是实体类 不是数据表名 2.字段应该用实体类中的字段 而不是数据表中的属性 实体如下 hql使 ...
- spring data jpa封装specification实现简单风格的动态查询
github:https://github.com/peterowang/spring-data-jpa-demo 单一实体的动态查询: @Servicepublic class AdvancedUs ...
- Spring Data JPA 复杂/多条件组合查询
1: 编写DAO类或接口 dao类/接口 需继承 public interface JpaSpecificationExecutor<T> 接口: 如果需要分页,还可继承 public ...
- Spring Data JPA 实现多表关联查询
本文地址:https://liuyanzhao.com/6978.html 最近抽出时间来做博客,数据库操作使用的是 JPA,相对比 Mybatis 而言,JPA 单表操作非常方便,增删改查都已经写好 ...
- JPA使用Specification构建动态查询
封装Specification查询条件,在Spring Data JPA 2.0以前使用 Specifications 这个辅助类来操作where.not.and和or连接,在2.0版本以后这个类会被 ...
- spring data jpa 使用JPQL的方式查询
用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询 @Que ...
- Spring Data JPA 的配置文件 已经数据库的状态
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
随机推荐
- Android 开源库 GitHub 托管
本文微信公众号「AndroidTraveler」首发. 背景 之前给大家写过一篇文章 Android 上传开源项目到 jcenter 实战踩坑之路,分享了上传开源项目到 jcenter 上面的一些踩坑 ...
- ElasticSearch(一):基本概念
ElasticSearch(一):基本概念 学习课程链接<Elasticsearch核心技术与实战> 基本概念示意图 索引与文档更偏向于开发人员的视角,属于逻辑上的一种概念:节点与分片更偏 ...
- Elastic Stack 开源的大数据解决方案
目的 本文主要介绍的内容有以下三点: 一. Elastic Stack是什么以及组成部分 二. Elastic Stack前景以及业务应用 三. Elasticsearch原理(索引方向) 四. El ...
- python 类内部装饰器的实现 与 参数解构学习
学习了函数的装饰器的写法,然后想到如果要在类中初始化或获取信息时能用装饰器做过滤和验证应该怎么写呢, 在网上查了下相关信息,感觉这样也是可以的,不知道会不会有什么问题class Ctj(): clas ...
- it公司比较
1:本人西电通院2013届毕业硕士,根据今年找工作的情况以及身边同学的汇总,总结各大公司的待遇如下,吐血奉献给各位学弟学妹,公司比较全,你想去的公司不在这里面,基本上是无名小公司了:但无名小公司有时也 ...
- 数据可视化:绘图库-Matplotlib
为什么要绘图? 一个图表数据的直观分析,下面先看一组北京和上海上午十一点到十二点的气温变化数据: 数据: 这里我用一段代码生成北京和上海的一个小时内每分钟的温度如下: import random co ...
- egret编译速度慢解决方法
egret编译速度慢解决方法 直接用增量更新egret run -a 每次改完代码 保存都会自动编译
- Hybrid App: 看看第三方WebViewJavascriptBridge是如何来实现Native和JavaScript交互
一.简介 在前面两篇文章中已经介绍了Native与JavaScript交互的几种方式,依次是JavaScriptCore框架.UI组件UIWebView.WebKit框架,这几种方式都是苹果公司提供的 ...
- element 动态合并表格
前言 element 官方的例子太简单了,不满足实际的需求 数据肯定是动态的,合并的行数,列数都是动态的,该如何知道每一行的合并数呢 需求 动态合并表格,数据来源于数据库 正文 一开始,我的数据源是单 ...
- 第一篇: openJDK源码编译安装--mac版本
1.为什么要编译JDK 想要一探JDK内部的实现机制,最便捷的路径之一就是自己编译一套JDK,通过阅读和跟踪调试JDK源码去了解Java技术体系的原理,虽然门槛高一点,但肯定比阅读各种书籍,文章,博客 ...