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:

 

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:

 

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

 

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;

下面是查询方法

 

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> {}

 

原文出处:https://www.cnblogs.com/happyday56/p/4661839.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值