Spring data JPA中使用Specifications动态构建查询

有时我们在查询某个实体的时候,给定的条件是不固定的,这是我们就需要动态 构建相应的查询语句,在JPA2.0中我们可以通过Criteria接口查询,JPA criteria查询.相比JPQL,其优势是类型安全,更加的面向对象.而在Spring data JPA中相应的接口是JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。 Specification接口中只定义了如下一个方法:

Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);

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:

@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略
}

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:

@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略
}

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

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;

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;

下面是查询方法

@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();
}
};
}

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的接口

public interface ArticleRepository extends JpaRepository<Article,Integer>,JpaSpecificationExecutor<Article> {}

1

public interface ArticleRepository extends JpaRepository<Article,Integer>,JpaSpecificationExecutor<Article> {}

通过以上的步骤,我们就能构建相应的查询sql了,使用这个接口要对JPA2.0中Criteria查询有一定的了解

时间: 2024-08-01 21:13:39

Spring data JPA中使用Specifications动态构建查询的相关文章

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

首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml配置文件.我现在使用的是spring boot ,没有了xml文件配置就方便多了.我同样尝试了两种方式,也都是简单的查询,需要更复杂的查询,还需要我研究研究.往下看,需要先配置springboot的开发环境,需要大致了解springboot,这里可以看下面两篇文章: springboot 项目新建 springboot

Spring data jpa中Query和@Query分别返回map结果集

引用: http://blog.csdn.net/yingxiake/article/details/51016234 http://blog.csdn.net/yingxiake/article/details/51016234 http://www.cnblogs.com/zj0208/p/6008627.html Query的使用: 在JPA 2.0 中我们可以使用entityManager.createNativeQuery()来执行原生的SQL语句. 但当我们查询结果没有对应实体类时,

Spring Data JPA中踩过的坑

说老实话,Spring Data JPA很好用,上次使用还是2013年,当时只是完成Java Bean和数据库中表的映射. 最近想起来用Spring Data JPA的起因是手头有一个项目,源代码是用原生SQL+JDBC实现的,在第一次部署时要初始化数据库,还hardcode了很多数据库配置参数.正好最近有空,就打算用Spring Boot Data JPA(spring-boot-starter-data-jpa)改造一下,仔细看了一下源代码发现和几年前已经天差地别,如果你的业务逻辑不是特别复

spring data jpa 创建方法名进行简单查询

Spring Data JPA框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find.findBy.read.readBy.get.getBy,然后对剩下部分进行解析. 假如创建如下的查询:findByTaskProjectName(),框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为Doc 1.先判断 taskProjectName (根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询:如果没有该属

Spring Data JPA 中使用Update Query更新实体类问题

在jpa中使用@Modifying 虽然事务已经能够更新,但是在循环更新的时候,执行modify语句后的查询的实体仍然是没有更新的. 执行完modifying query, EntityManager可能会包含过时的数据,因为EntityManager不会自动清除实体.只有添加clearAutomatically属性,EntityManager才会自动清除实体对象. @Modifying(clearAutomatically = true)

序列化表单为json对象,datagrid带额外参提交一次查询 后台用Spring data JPA 实现带条件的分页查询

查询窗口中可以设置很多查询条件 表单中输入的内容转为datagrid的load方法所需的查询条件向原请求地址再次提出新的查询,将结果显示在datagrid中 转换方法看代码注释 <td colspan="2"><a id="searchBtn" href="#" class="easyui-linkbutton" data-options="iconCls:'icon-search'"&g

spring data JPA 中的多属性排序---养老院管理系统涉及技术整理

在此介绍我所用的一种方式: 第一步,引包 import org.springframework.data.domain.Sort;import org.springframework.data.domain.Sort.Order; 第二步,service方法代码 @Override public Page<TurnOverRecord> findAll(Integer pageNum, Integer pageSize, TurnOverRecord turnOverRecord) {//先按

Spring Data JPA中的mappedBy

mappedBy单向关系不需要设置该属性,双向关系必须设置,避免双方都建立外键字段 数据库中一对多的关系,关联关系总是被多方维护的即外键建在多方,我们在单方对象的@OneToMany(mappedBy="xxx") mappedBy 单向关系不需要设置该属性,双向关系必须设置,避免双方都建立外键字段 数据库中一对多的关系,关联关系总是被多方维护的即外键建在多方,我们在单方对象的@OneToMany(mappedby="xxx ") 把关系的维护交给多方对象的属性去维

【spring data jpa】spring data jpa 中的update 更新字段,如果原字段值为null不处理,不为null则在原来的值上加一段字符串

示例代码: /** * 如果barCode字段值为null则不处理 * 如果barCode字段值不为null则在原本值的前面拼接 del: * @param dealer * @return */ @Modifying @Transactional @Query("update Dealer d set " + " d.deleteFlag = 1 ," + " d.barCode = CASE WHEN d.barCode IS NULL THEN d.