在 JPQL Query 中设置可选参数

Nac*_*tin 6 java jpa filter optional-parameters jpql

我得到了我想用 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;
}
Run Code Online (Sandbox Code Playgroud)

Jes*_*sta 8

在阅读了 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);

Run Code Online (Sandbox Code Playgroud)

请注意这里如何??,我使用 OR 语句基于我的参数可以呈现两种形式,FilterSuperAwesome 或 NULL 的实例。当为 NULL 时,条件始终为 True,就好像它不存在一样。

这在 JHipster 项目的 JpaRepository 类中工作得很好。

  • 我们曾经使用这个解决方案一段时间,直到我们进行了负载测试并意识到,由于某种原因,在不使用可选参数的情况下,这会大大降低查询的性能。(大表,几百万个条目,DB2,大约四/五个可选参数)。 (3认同)

Ily*_*hin 4

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 
Run Code Online (Sandbox Code Playgroud)