有時我們在查詢某個實體的時候,給定的條件是不固定的,這是我們就需要動態 構建相應的查詢語句,在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> {} |