【问题标题】:Set optional parameters in JPQL Query在 JPQL 查询中设置可选参数
【发布时间】:2018-11-07 05:12:02
【问题描述】:

I got this list of coins that i want to filter with that 3 optional parameters(Currency, quantity and year)

如何将 JPQL 中的参数设置为可选参数?我不想做 9 个“if else”来检查它是否为空

我得到了函数 filtradoMonedas (filterCoins),它使用这 3 个可选参数过滤对象 Moneda(coin),但如果有一个空参数,它就不起作用。

如果不设置空参数,这很好用,如果 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;
}

【问题讨论】:

    标签: java jpa filter jpql optional-parameters


    【解决方案1】:

    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 
    

    【讨论】:

    • ano != null 表示人类,ano == null 表示非人类我猜?
    【解决方案2】:

    在阅读了 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 类中工作得很好。

    【讨论】:

    • 不错!聪明的解决方案。我处于类似的情况,最终得到了一些带有大量 if 语句的丑陋代码。这应该可以解决问题。
    • 我们曾经使用此解决方案一段时间,直到我们进行负载测试并意识到由于某种原因,在不使用可选参数的情况下,这会大大降低查询的性能。 (大表,几百万条目,DB2,大约四/五个可选参数)。
    • @Bernhard 你能提供更多信息吗?您找到这种行为的原因了吗?
    • @Wecherowski 很遗憾,不,我对数据库内部结构不是很熟悉,这只是……。我们使用本机查询观察并重写查询,您可以在其中使用实际的可选参数,从而显着提高性能。
    • 这可能与 = 一起使用,因为它只是评估为 false,但是,它不适用于 IN,因为 IN 运算符不允许空列表或 null 在值方面(至少在 postgres 中) ,这是我问谷歌的,它把我带到了这里:-D
    【解决方案3】:

    在我的情况下,我的可选参数是List&lt;String&gt;,解决方案如下:

    @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 运算符
    • 如果 parameter categories null,这意味着我必须为 选择所有值COLUMN_X,将始终在 OR 运算符的右侧返回 TRUE

    为什么是 COALESCE 以及为什么里面有一个 null 值?

    让我们在所有条件下探索WHERE 子句:

    案例一: categories = null

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

    OR的左边部分会返回false,而OR的右边部分会一直返回true,其实COALESCE会返回第一个非如果存在 null 值,如果所有参数为 null,则返回 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(即为假)。

    为什么将 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' 的行。

    【讨论】:

    • 您是否针对 MSSQL 进行过检查?根据我的经验,这可能适用于 Oracle 和 PostgreSQL,但对于 MSSQL,如果会失败,因为它仅在合并中不允许空值。只需致电select coalesce(null, null) 即可查看。
    • 嗨@kulas,不,我不会尝试针对 MSSQL,只有 Oracle。
    猜你喜欢
    • 1970-01-01
    • 2016-03-29
    • 2014-10-18
    • 2014-09-23
    • 2012-06-21
    • 2021-08-22
    • 2011-01-17
    • 1970-01-01
    • 2021-09-21
    相关资源
    最近更新 更多