Spring data JPA中使用Specifications动态构建查询
有时我们在查询某个实体的时候,给定的条件是不固定的,这是我们就需要动态 构建相应的查询语句,在JPA2.0中我们可以通过Criteria接口查询,JPA criteria查询.相比JPQL,其优势是类型安全,更加的面向对象.而在Spring data JPA中相应的接口是JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。 Specification接口中只定义了如下一个方法:
1
|
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
|
我们只需要重写这个方法即可,相关知识请自行查阅JPA Criteria查询
过滤条件
1:过滤条件会被应用到SQL语句的FROM子句中。在criteria 查询中,查询条件通过Predicate或Expression实例应用到CriteriaQuery对象上。
2:这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上
3:CriteriaBuilder也作为Predicate实例的工厂,通过调用CriteriaBuilder 的条件方法( equal,notEqual, gt, ge,lt, le,between,like等)创建Predicate对象。
4:复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。
相关代码如下,在这个例子中我们定义了2个类Articel和User类
Article:
@Table(name = "t_article")
public class Article implements Serializable{
private static final long serialVersionUID = 6112067846581696118L;
@Id
@GeneratedValue
private Integer aid;
private String title;
@Temporal(TemporalType.TIMESTAMP)
private Date postTime;
@Temporal(TemporalType.TIMESTAMP)
private Date lastEditTime;
private String ip;
private String tag;
private boolean forbidComment;//禁止评论
@ManyToOne
@JoinColumn(name = "uid")
private User user;
private boolean recommend;//是否是推荐
@Temporal(TemporalType.TIMESTAMP)
private Date recommendTime;//推荐时间
//setter/getter略
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Entity
@Table(name = "t_article")
public class Article implements Serializable{
private static final long serialVersionUID = 6112067846581696118L;
@Id
@GeneratedValue
private Integer aid;
private String title;
@Temporal(TemporalType.TIMESTAMP)
private Date postTime;
@Temporal(TemporalType.TIMESTAMP)
private Date lastEditTime;
private String ip;
private String tag;
private boolean forbidComment;//禁止评论
@ManyToOne
@JoinColumn(name = "uid")
private User user;
private boolean recommend;//是否是推荐
@Temporal(TemporalType.TIMESTAMP)
private Date recommendTime;//推荐时间
//setter/getter略
}
|
User:
@Table(name = "t_user")
public class User implements Serializable {
private static final long serialVersionUID = 3703405133265901053L;
@Id
@GeneratedValue
private Integer uid;
private String nickname;
private String password;
//setter/getter略
}
1
2
3
4
5
6
7
8
9
10
11
12
|
@Entity
@Table(name = "t_user")
public class User implements Serializable {
private static final long serialVersionUID = 3703405133265901053L;
@Id
@GeneratedValue
private Integer uid;
private String nickname;
private String password;
//setter/getter略
}
|
其中user和article是一对多的关系,是单向的
封装的查询实体SearchArticle
private static final long serialVersionUID = -1082122462716689486L;
private int page = 1;
private int limit;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss")
private Date postTimeStart;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss")
private Date postTimeEnd;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss")
private Date recTimeStart;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss")
private Date recTimeEnd;
private String nickname;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class SearchArticle implements Serializable{
private static final long serialVersionUID = -1082122462716689486L;
private int page = 1;
private int limit;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss")
private Date postTimeStart;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss")
private Date postTimeEnd;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss")
private Date recTimeStart;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME,pattern = "yyyy-MM-dd HH:mm:ss")
private Date recTimeEnd;
private String nickname;
|
下面是查询方法
private ArticleRepository articleRepository;
@Override
public QueryResult<ArticleModel> findArticle(SearchArticle
searchArticle) {
Sort sort = new Sort(Sort.Direction.DESC,"postTime");
Specification<Article> specification =
getWhereClause(searchArticle);
Page<Article> all =
articleRepository.findAll(specification, new
PageRequest(searchArticle.getPage() - 1,
searchArticle.getLimit(),sort));
QueryResult<ArticleModel> result = new
QueryResult<>();
List<ArticleModel> list = new
ArrayList<>(searchArticle.getLimit());
for (Article article:all.getContent()){
ArticleModel model = new
ArticleModel(article.getAid(),article.getTitle(),article.getPostTime(),article.isRecommend(),
article.getRecommendTime(),article.getIp(),article.getUser().getUid(),article.getUser().getNickname());
list.add(model);
}
result.setRows(list);
result.setTotal(all.getTotalElements());
return result;
}
/**
* 动态生成where语句
* @param searchArticle
* @return
*/
private Specification<Article> getWhereClause(final
SearchArticle searchArticle){
return new Specification<Article>() {
@Override
public Predicate toPredicate(Root<Article> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicate = new
ArrayList<>();
if(searchArticle.getPostTimeStart()!=null){
predicate.add(cb.greaterThanOrEqualTo(root.get("postTime").as(Date.class),
searchArticle.getPostTimeStart()));
}
if(searchArticle.getPostTimeEnd()!=null){
predicate.add(cb.lessThanOrEqualTo(root.get("postTime").as(Date.class),
searchArticle.getPostTimeEnd()));
}
if(searchArticle.getRecTimeStart()!=null){
predicate.add(cb.greaterThanOrEqualTo(root.get("recommendTime").as(Date.class),
searchArticle.getRecTimeStart()));
}
if (searchArticle.getRecTimeEnd()!=null){
predicate.add(cb.lessThanOrEqualTo(root.get("recommendTime").as(Date.class),
searchArticle.getRecTimeEnd()));
}
if
(StringUtils.isNotBlank(searchArticle.getNickname())){
//两张表关联查询
Join<Article,User> userJoin =
root.join(root.getModel().getSingularAttribute("user",User.class),JoinType.LEFT);
predicate.add(cb.like(userJoin.get("nickname").as(String.class), "%" +
searchArticle.getNickname() + "%"));
}
Predicate[] pre = new Predicate[predicate.size()];
return
query.where(predicate.toArray(pre)).getRestriction();
}
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
@Autowired
private ArticleRepository articleRepository;
@Override
public QueryResult<ArticleModel> findArticle(SearchArticle searchArticle) {
Sort sort = new Sort(Sort.Direction.DESC,"postTime");
Specification<Article> specification = getWhereClause(searchArticle);
Page<Article> all = articleRepository.findAll(specification, new PageRequest(searchArticle.getPage() - 1, searchArticle.getLimit(),sort));
QueryResult<ArticleModel> result = new QueryResult<>();
List<ArticleModel> list = new ArrayList<>(searchArticle.getLimit());
for (Article article:all.getContent()){
ArticleModel model = new ArticleModel(article.getAid(),article.getTitle(),article.getPostTime(),article.isRecommend(),
article.getRecommendTime(),article.getIp(),article.getUser().getUid(),article.getUser().getNickname());
list.add(model);
}
result.setRows(list);
result.setTotal(all.getTotalElements());
return result;
}
/**
* 动态生成where语句
* @param searchArticle
* @return
*/
private Specification<Article> getWhereClause(final SearchArticle searchArticle){
return new Specification<Article>() {
@Override
public Predicate toPredicate(Root<Article> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicate = new ArrayList<>();
if(searchArticle.getPostTimeStart()!=null){
predicate.add(cb.greaterThanOrEqualTo(root.get("postTime").as(Date.class), searchArticle.getPostTimeStart()));
}
if(searchArticle.getPostTimeEnd()!=null){
predicate.add(cb.lessThanOrEqualTo(root.get("postTime").as(Date.class), searchArticle.getPostTimeEnd()));
}
if(searchArticle.getRecTimeStart()!=null){
predicate.add(cb.greaterThanOrEqualTo(root.get("recommendTime").as(Date.class), searchArticle.getRecTimeStart()));
}
if (searchArticle.getRecTimeEnd()!=null){
predicate.add(cb.lessThanOrEqualTo(root.get("recommendTime").as(Date.class), searchArticle.getRecTimeEnd()));
}
if (StringUtils.isNotBlank(searchArticle.getNickname())){
//两张表关联查询
Join<Article,User> userJoin = root.join(root.getModel().getSingularAttribute("user",User.class),JoinType.LEFT);
predicate.add(cb.like(userJoin.get("nickname").as(String.class), "%" + searchArticle.getNickname() + "%"));
}
Predicate[] pre = new Predicate[predicate.size()];
return query.where(predicate.toArray(pre)).getRestriction();
}
};
}
|
其中的 ArticleRepository接口如下,spring data jpa不需要你自己实现dao的接口
1
|
public interface ArticleRepository extends JpaRepository<Article,Integer>,JpaSpecificationExecutor<Article> {}
|
通过以上的步骤,我们就能构建相应的查询sql了,使用这个接口要对JPA2.0中Criteria查询有一定的了解
Spring data JPA中使用Specifications动态构建查询的更多相关文章
- Spring Data JPA,一种动态条件查询的写法
我们在使用SpringData JPA框架时,进行条件查询,如果是固定条件的查询,我们可以使用符合框架规则的自定义方法以及@Query注解实现. 如果是查询条件是动态的,框架也提供了查询接口. Jpa ...
- Spring Data JPA中的动态查询 时间日期
功能:Spring Data JPA中的动态查询 实现日期查询 页面对应的dto类private String modifiedDate; //实体类 @LastModifiedDate protec ...
- SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法
软件152 尹以操 首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml ...
- 【hql】spring data jpa中 @Query使用hql查询 问题
spring data jpa中 @Query使用hql查询 问题 使用hql查询, 1.from后面跟的是实体类 不是数据表名 2.字段应该用实体类中的字段 而不是数据表中的属性 实体如下 hql使 ...
- Spring data jpa中Query和@Query分别返回map结果集
引用: http://blog.csdn.net/yingxiake/article/details/51016234 http://blog.csdn.net/yingxiake/article/d ...
- 【tmos】spring data jpa 创建方法名进行简单查询
参考链接 spring data jpa 创建方法名进行简单查询:http://www.cnblogs.com/toSeeMyDream/p/6170790.html
- 如何在Spring Data JPA中引入Querydsl
一.环境说明 基础框架采用Spring Boot.Spring Data JPA.Hibernate.在动态查询中,有一种方式是采用Querydsl的方式. 二.具体配置 1.在pom.xml中,引入 ...
- Spring Data JPA中CrudRepository与JpaRepository的不同
使用Spring Data JPA CrudRepository 和JpaRepository 的好处: 继承这些接口,可以使Spring找到自定义的数据库操作接口,并生成代理类,后续可以注入到Spr ...
- 在Spring Data JPA 中使用Update Query更新实体类
对于 Spring Data JPA 使用的时间不长,只有两年时间.但是踩过坑的却不少. 使用下列代码 @Modifying @Query("update User u set u.firs ...
随机推荐
- eMMC基础技术11:flash memory
[转]http://www.wowotech.net/basic_tech/367.html 0.前言 eMMC 是 Flash Memory 的一类,在详细介绍 eMMC 之前,先简单介绍一下 Fl ...
- 华为交换机配置NTP服务端/客户端
作者:邓聪聪 配置设备作为NTP服务器 单播客户端/服务器模式 # 配置NTP主时钟,层数为2. <HUAWEI> system-view [HUAWEI] ntp refclock-ma ...
- python3+selenium框架设计04-封装测试基类
在完成了日志类封装之后,那我们就要对测试基类进行实现,在其中对一些请求再次封装,在项目下新建一个framework文件夹,在文件夹下新建Base_Page.py文件,这是用来写测试基类的文件.在项目下 ...
- 安装mysql5.7与创建用户和远程登录授权
环境:ubuntu18.04 参考文章:安装并远程登录授权:https://www.cnblogs.com/chancy/p/9444187.html 用户管理:https://www.cnblogs ...
- inode索引详解
理解inode inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础. 我觉得,理解inode,不仅有助于提高系统操作水平,还有助于体会Unix设计哲学,即如何把底层的复杂性抽 ...
- VUE 浏览器关闭时清空localStorage
1. 概述 1.1 说明 vue项目中,为了解决页面刷新时vuex数据丢失问题,使用localStorage进行存储对应的vuex数据(判断对应localStorage是否为空,不为空则为vuex中数 ...
- Python装饰器执行顺序详解
探究多个装饰器执行顺序 装饰器是Python用于封装函数或代码的工具,网上可以搜到很多文章可以学习,我在这里要讨论的是多个装饰器执行顺序的一个迷思. 疑问 大部分涉及多个装饰器装饰的函数调用顺序时都会 ...
- Confluence 6 已经存在的 Confluence 安装配置一个数据源连接
如果你希望在使用 JDBC 直接方式的应用中切换到使用数据源: 停止 Confluence. 备份下面的文件,以防止你可能需要重新恢复你的配置: <installation-directory& ...
- xampp 安装以及相关问题
1.安装xampp 说明:xampp集成了mysql,Apache,php,360软件里面就有 2.mysql端口被占用. 如果电脑上已安装MySql数据库,还想用XAM ...
- LeetCode(103): 二叉树的锯齿形层次遍历
Medium! 题目描述: 给定一个二叉树,返回其节点值的锯齿形层次遍历.(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行). 例如:给定二叉树 [3,9,20,null,nul ...