【问题标题】:using a ParameterExpression versus a variable in JPA Criteria API在 JPA Criteria API 中使用 ParameterExpression 与变量
【发布时间】:2013-05-08 10:31:01
【问题描述】:

在使用 JPA Criteria API 时,直接使用 ParameterExpression 而非变量有什么优势?例如。当我希望在字符串变量中按名称搜索客户时,我可以写类似

private List<Customer> findCustomer(String name) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Customer> criteriaQuery = cb.createQuery(Customer.class);
    Root<Customer> customer = criteriaQuery.from(Customer.class);
    criteriaQuery.select(customer).where(cb.equal(customer.get("name"), name));
    return em.createQuery(criteriaQuery).getResultList();
}

有了参数,这就变成了:

private List<Customer> findCustomerWithParam(String name) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Customer> criteriaQuery = cb.createQuery(Customer.class);
    Root<Customer> customer = criteriaQuery.from(Customer.class);
    ParameterExpression<String> nameParameter = cb.parameter(String.class, "name");
    criteriaQuery.select(customer).where(cb.equal(customer.get("name"), nameParameter));
    return em.createQuery(criteriaQuery).setParameter("name", name).getResultList();
}

为了简洁起见,我更喜欢第一种方式,尤其是当查询因可选参数而变长时。使用像 SQL 注入这样的参数有什么缺点吗?

【问题讨论】:

  • 我不能代表 JPA,但我发现 OpenJPA 在内部将 Criteria 查询转换为 JPQL,这可以通过使用 OpenJPA 特定的功能来打印(参见 openjpa.apache.org/builds/2.1.1/apache-openjpa/docs/…)。第一个查询转换为“SELECT c FROM Customer c WHERE c.name = 'test Customer'”。这意味着它不使用参数,因此如果将其进一步转换为 SQL,则相应的准备好的语句将不使用参数。第二个版本翻译为 JPQL “SELECT c FROM Customer c WHERE c.name = :name”,所以我将使用参数。
  • 经过更多测试后,我发现使用 JPQL 编写相同的查询并使用名称“' OR 'x'='x”会注入 JPQL。使用条件 API 时,OpenJPA 记录的生成的 JPQL 看起来完全相同。然而,OpenJPA 记录的实际 SQL 然后使用带有参数值“' OR 'x'='x”的预准备语句,而不是 JPQL 案例中的 ''。这意味着 SQL 注入在这里不起作用!不幸的是,我不知道这有多可靠。这似乎是一个未记录的功能。
  • 提示:我刚刚尝试了querydsl.com,它的语法更加简洁易读。似乎默认使用参数来防范sql注入。
  • 也就是说,如果不使用参数,是否可以进行SQL注入?

标签: java api jpa criteria


【解决方案1】:

您可以像这样使用 ParameterExpression: 假设你有一些输入过滤器,一个例子可能是这样的:

  • 在您的查询中,您必须检查财政代码的值。

让我们开始吧: 首先创建 criteriaQuery 和 criteriaBuilder 和 root

        CriteriaBuilder cb = _em.getCriteriaBuilder();
        CriteriaQuery<Tuple> cq = cb.createTupleQuery();
        Root<RootEntity> soggettoRoot = cq.from(RootEntity.class);

1) 初始化一个 predicateList(用于 where 子句)和一个 paramList(用于 param)

Map<ParameterExpression,String> paramList = new HashMap();
List<Predicate> predicateList = new ArrayList<>();

2 )检查输入是否为null并创建predicateList和param

if( input.getFilterCF() != null){
            //create ParameterExpression
            ParameterExpression<String> cf = cb.parameter(String.class);


           //if like clause
            predicateList.add(cb.like(root.<String>get("cf"), cf));
            paramList.put(cf , input.getFilterCF() + "%");

           //if equals clause
           //predicateList.add(cb.equal(root.get("cf"), cf));   
           //paramList.put(cf,input.getFilterCF()());
        }

3) 创建 where 子句

 cq.where(cb.and(predicateList.toArray(new   Predicate[predicateList.size()])));
TypedQuery<Tuple> q = _em.createQuery(cq);

4) 设置参数值

        for(Map.Entry<ParameterExpression,String> entry : paramList.entrySet())
        {
            q.setParameter(entry.getKey(), entry.getValue());
        }

【讨论】:

  • 不使用参数是否可以进行SQL注入?
【解决方案2】:

使用参数时,很可能(取决于 JPA 实现、使用中的数据存储和 JDBC 驱动程序)SQL 将优化为 JDBC 参数,因此如果您使用不同的参数值执行相同的操作,它使用相同的参数JDBC 语句。

SQL 注入始终取决于开发人员是否验证某些用作参数的用户输入。

【讨论】:

  • 这是一件好事。我更喜欢更简单的代码而不是优化的代码,直到它被识别为问题。在 where 子句中为可选条件使用参数时遇到的问题是,您需要重复使用“if (optionalParameter != null)”之类的代码来声明和设置参数。你的第二个答案让我很困惑。我一直认为(直到可能的实现错误)参数可以保证不会遭受 SQL 注入攻击,我想知道我更简单的第一种方法是否会遭受 SQL 注入攻击。顺便说一句,我正在使用 OpenJPA。
【解决方案3】:

在第二种情况下,您不必要地使用了 ParameterExpression。 CriteriaQuery 的 where() 方法接受一个 Predicate 作为其参数。

而不是这个:

ParameterExpression<String> nameParameter = cb.parameter(String.class, "name");
criteriaQuery.select(customer).where(cb.equal(customer.get("name"), nameParameter));
return em.createQuery(criteriaQuery).setParameter("name", name).getResultList();

你可以简单地使用这个:

Predicate predicate = cb.equal(customer.get("name"), name);
criteriaQuery.select(customer).where(predicate);
return em.createQuery(criteriaQuery).getResultList();

ParameterExpression 能够通过其 in()、isNull()、isNotNull() 方法为您生成谓词,请参阅documentation。在我看来,这将是它的主要优势。

注意:可以像您一样使用 ParameterExpression,这相当于将命名参数传递给 JPQL 查询。

【讨论】:

    猜你喜欢
    • 2014-02-19
    • 1970-01-01
    • 2013-06-21
    • 1970-01-01
    • 1970-01-01
    • 2012-08-19
    • 2018-06-04
    • 1970-01-01
    相关资源
    最近更新 更多