Spring-Data-JPA criteria 查詢

Spring Data JPA雖然大大的簡化了持久層的開發,可是在實際開發中,不少地方都須要高級動態查詢html

Criteria APIjava

Criteria 查詢是以元模型的概念爲基礎的,元模型是爲具體持久化單元的受管實體定義的,這些實體能夠是實體類,嵌入類或者映射的父類。安全

CriteriaQuery接口:表明一個specific的頂層查詢對象,它包含着查詢的各個部分,好比:select 、from、where、group by、order by等注意:CriteriaQuery對象只對實體類型或嵌入式類型的Criteria查詢起做用數據結構

Root接口:表明Criteria查詢的根對象,Criteria查詢的查詢根定義了實體類型,能爲未來導航得到想要的結果,它與SQL查詢中的FROM子句相似less

​ 1:Root實例是類型化的,且定義了查詢的FROM子句中可以出現的類型。maven

​ 2:查詢根實例能經過傳入一個實體類型給 AbstractQuery.from方法得到。ide

​ 3:Criteria查詢,能夠有多個查詢根。ui

​ 4:AbstractQuery是CriteriaQuery 接口的父類,它提供獲得查詢根的方法。CriteriaBuilder接口:用來構建CritiaQuery的構建器對象Predicate:一個簡單或複雜的謂詞類型,其實就至關於條件或者是條件組合spa

若是編譯器可以對查詢執行語法正確性檢查,那麼對於 Java 對象而言該查詢就是類型安全的。Java™Persistence API (JPA) 的 2.0 版本引入了 Criteria API,這個 API 首次將類型安全查詢引入到 Java 應用程序中,併爲在運行時動態地構造查詢提供一種機制。hibernate

JPA元模型

在JPA中,標準查詢是以元模型的概念爲基礎的.元模型是爲具體持久化單元的受管實體定義的.這些實體能夠是實體類,嵌入類或者映射的父類.提供受管實體元信息的類就是元模型類.

使用元模型類最大的優點是憑藉其實例化能夠在編譯時訪問實體的持久屬性.該特性使得criteria 查詢更加類型安全.

以下,Item實體類對應的元模型Item_

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Item.class)
public abstract class Item_ {
   public static volatile SingularAttribute<Item, Integer> itemId;
   public static volatile SingularAttribute<Item, String> itemName;
   public static volatile SingularAttribute<Item, Integer> itemStock;
   public static volatile SingularAttribute<Item, Integer> itemPrice;
}複製代碼

這樣的元模型不用手動建立,在Maven中添加插件,編譯以後@Entity註解的類就會自動生成對應的元模型

<!--hibernate JPA 自動生成元模型-->
<!-- 相關依賴 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>5.2.10.Final</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.1.0.Final</version>
        </dependency>複製代碼
<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
       <configuration>
           <source>1.8</source>
             <target>1.8</target>
              <compilerArguments>
                 <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
              </compilerArguments>
         </configuration>
 </plugin>複製代碼

方法不止一種,具體見:Chapter 2. Usage

使用criteria 查詢簡單Demo

@Service
public class ItemServiceImpl implements ItemService {

    @Resource
    private EntityManager entityManager;

    @Override
    public List<Item> findByConditions(String name, Integer price, Integer stock) {
          //建立CriteriaBuilder安全查詢工廠
        //CriteriaBuilder是一個工廠對象,安全查詢的開始.用於構建JPA安全查詢.
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        //建立CriteriaQuery安全查詢主語句
        //CriteriaQuery對象必須在實體類型或嵌入式類型上的Criteria 查詢上起做用。
        CriteriaQuery<Item> query = criteriaBuilder.createQuery(Item.class);
        //Root 定義查詢的From子句中能出現的類型
        Root<Item> itemRoot = query.from(Item.class);
          //Predicate 過濾條件 構建where字句可能的各類條件
          //這裏用List存放多種查詢條件,實現動態查詢
        List<Predicate> predicatesList = new ArrayList<>();
          //name模糊查詢 ,like語句
        if (name != null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.like(
                                    itemRoot.get(Item_.itemName), "%" + name + "%")));
        }
         // itemPrice 小於等於 <= 語句
        if (price != null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.le(
                                    itemRoot.get(Item_.itemPrice), price)));
        }
        //itemStock 大於等於 >= 語句
        if (stock != null) {
            predicatesList.add(
                    criteriaBuilder.and(
                            criteriaBuilder.ge(
                                    itemRoot.get(Item_.itemStock), stock)));
        }
          //where()拼接查詢條件
        query.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
        TypedQuery<Item> typedQuery = entityManager.createQuery(query);
        List<Item> resultList = typedQuery.getResultList();
        return resultList;
    }
}複製代碼

criteriaBuilder中各方法對應的語句

equle : filed = value

gt / greaterThan : filed > value

lt / lessThan : filed < value

ge / greaterThanOrEqualTo : filed >= value

le / lessThanOrEqualTo: filed <= value

notEqule : filed != value

like : filed like value

notLike : filed not like value

若是每一個動態查詢的地方都這麼寫,那就感受太麻煩了.

那實際上,在使用Spring Data JPA的時候,只要咱們的Repo層接口繼承JpaSpecificationExecutor接口就可使用Specification進行動態查詢了,咱們先看下JpaSpecificationExecutor接口:

public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> var1);

    List<T> findAll(Specification<T> var1);

    Page<T> findAll(Specification<T> var1, Pageable var2);

    List<T> findAll(Specification<T> var1, Sort var2);

    long count(Specification<T> var1);
}複製代碼

在這裏有個很重要的接口Specification

public interface Specification<T> {
    Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}複製代碼

這個接口只有一個方法,返回動態查詢的數據結構,用於構造各類動態查詢的SQL

Specification接口示例

public Page<Item> findByConditions(String name, Integer price, Integer stock, Pageable page) {
     Page<Item> page = itemRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicatesList = new ArrayList<>();
            //name模糊查詢 ,like語句
            if (name != null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.like(
                                        root.get(Item_.itemName), "%" + name + "%")));
            }
            // itemPrice 小於等於 <= 語句
            if (price != null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.le(
                                        root.get(Item_.itemPrice), price)));
            }
            //itemStock 大於等於 >= 語句
            if (stock != null) {
                predicatesList.add(
                        criteriaBuilder.and(
                                criteriaBuilder.ge(
                                        root.get(Item_.itemStock), stock)));
            }
            return criteriaBuilder.and(
                    predicatesList.toArray(new Predicate[predicatesList.size()]));
        }, page);
    return page;
}複製代碼

在這裏由於findAll(Specification<T> var1, Pageable var2)方法中參數 Specification<T> 是一個匿名內部類

那這裏就能夠直接用lambda表達式直接簡化代碼.

這樣寫,就比用CriteriaBuilder安全查詢工廠簡單多了.

調用:

Page<Item> itemPageList = findByConditions("車", 300, null, new PageRequest(1, 10));複製代碼

利用JPA的Specification<T>接口和元模型就實現動態查詢了.

那其實這樣每個須要動態查詢的地方都須要寫一個這樣相似的findByConditions方法,感受也很麻煩了.固然是越簡化越好.

下一篇將會講一個JPASpecification更方便的使用.
原文連接:Spring-Data-JPA criteria 查詢 | 火堯

相關文章
相關標籤/搜索