簡體   English   中英

在 JPQL Query 中設置可選參數

[英]Set optional parameters in JPQL Query

我得到了我想用 3 個可選參數(貨幣、數量和年份)過濾的硬幣列表

如何將 JPQL 中的參數設置為可選? 我不想做 9 個“if else”來檢查它是否為空

我得到了使用 3 個可選參數過濾對象 Moneda(coin) 的函數 filtradoMonedas (filterCoins),但如果有一個空參數,它就不起作用。

如果不設置空參數,如果 cantidad 或 ano 是 "" 返回錯誤查詢的異常,這只會很好地工作。 只是想把它作為一個可選的。 方法如下:

public List<Moneda> filtradoMonedas(Divisa divisa, BigDecimal cantidad, 
        BigDecimal ano){

    EntityManager em = getEntityManager();

    Query consulta = em.createQuery("SELECT m FROM Moneda m "
            + "WHERE m.divisa = :divisa "
            + "AND m.cantidad= :cantidad "
            + "AND m.ano = :ano");

    consulta.setParameter("divisa", divisa);
    consulta.setParameter("cantidad", cantidad);
    consulta.setParameter("ano", ano);

    List<Moneda> filtradoMonedas = (List<Moneda>) consulta.getResultList();
    // sincronizar los objetos devueltos con la base de datos
    for (Moneda m : filtradoMonedas) {
        em.refresh(m);
    }

    em.close();
    return filtradoMonedas;
}

在閱讀了 Ilya Dyoshin 的評論以及“您應該認為不是參數是可選的,而是條件是可選的”背后的聰明想法后我決定通過使用帶有 @Query 注釋的 JPQL 走自己的路,並創建一個運行良好的動態 SQL 查詢.

關鍵是應用一些 SQL 邏輯來使條件成為可選的而不是參數:

    @Query("SELECT a " +
           "FROM amazing " +
           "WHERE (:filterSuperAwesome IS NULL " +
                        "OR a.filterSuperAwesome = :filterSuperAwesome)"); 

    List<FilterSuperAwesome> filterAwesomeORSuperAwesome(
                    @Param("filterSuperAwesome ") FilterSuperAwesome filterSuperAwesome);

注意這里☝️,我使用 OR 語句是基於我的參數可以呈現兩種形式,FilterSuperAwesome 或 NULL 的實例。 當為 NULL 時,條件始終為 True,就好像它不存在一樣。

這在 JHipster 項目的 JpaRepository 類中工作得很好。

JPQL 不支持可選參數。

實際上,您應該認為不是參數是可選的,而是條件是可選的。 這個想法將引導您創建“動態”查詢。 這將引導您下一步從 JPQL 切換到 Criteria API。 這將導致你寫出這樣的東西:

    // Actually can be generated during build, and thus can be ommited
    @StaticMetamodel(Moneda.class)
    abstract class Moneda_ {
        public static volatile SingularAttribute<Moneda, BigDecimal> cantidad;
        public static volatile SingularAttribute<Moneda, Divisia> divisia;
        public static volatile SingularAttribute<Moneda, BigDecimal> ano;
    }

    final CriteriaBuilder cb = em.getCriteriaBuilder();

    final CriteriaQuery<Moneda> cq = cb.createQuery(Moneda.class);
    final Root<Moneda> root = cq.from(Moneda.class);

    Set<Predicate> predicates = new HashSet<>(3);
    if (cantidad != null) {
        predicates.add(cb.equal(root.get(Moneda_.cantidad), cantidad));
    }

    if (ano != null) {
        predicates.add(cb.equal(root.get(Moneda_.ano), ano));
    }

    if (divisia != null) {
        predicates.add(cb.equal(root.get(Moneda_.divisia), divisia));
    }

    cq.where(predicates.toArray(new Predicate[predicates.size()]));

    em.createQuery(cq).getResultList();

    // and do whatever you want 

就我而言,我遇到的問題是我的可選參數是List<String> ,解決方案如下:

@Query(value = "SELECT *
                FROM ...
                 WHERE (COLUMN_X IN :categories OR COALESCE(:categories, null) IS NULL)"
, nativeQuery = true)
List<Archive> findByCustomCriteria1(@Param("categories") List<String> categories);

這邊走:

  • 如果參數一個或多個值,則由OR運算符的左側選擇
  • 如果參數類別null ,這意味着我必須選擇COLUMN_X 的所有值,則OR運算符的右側將始終返回 TRUE

為什么COALESCE以及為什么它里面有一個值?

讓我們探索所有條件下的WHERE子句:

案例 1: categories = null

(COLUMN_X IN null OR COALESCE(null, null) IS NULL)

的左側將返回false,而右面部分將始終返回true,其實COALESCE將返回第一個非空值(如果存在)並返回null如果所有參數都為空。

案例 2: categories = ()

(COLUMN_X IN null OR COALESCE(null, null) IS NULL)

JPA 會自動將空列表識別為空值,因此結果與案例 1 相同。

案例 3: categories = ('ONE_VALUE')

(COLUMN_X IN ('ONE_VALUE') OR COALESCE('ONE_VALUE', null) IS NULL)

OR的左側部分將僅針對COLUMN_X = 'ONE_VALUE'那些值返回 true,而OR的右側部分將永遠不會返回 true,因為它等於'ONE_VALUE' IS NULL (即 false)。

為什么 null 作為第二個參數? 嗯,那是因為COALESCE至少需要兩個參數。

案例 4: categories = ('ONE_VALUE', 'TWO_VALUE')

(COLUMN_X IN ('ONE_VALUE', 'TWO_VALUE') OR COALESCE('ONE_VALUE', 'TWO_VALUE', null) IS NULL)

與案例 3 一樣, OR運算符的左側部分將僅選擇COLUMN_X等於'ONE_VALUE''TWO_VALUE'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM