如何使用JPA的标准API构建多列“WHERE ... IN”表达式?

时间:2013-03-04 16:08:11

标签: java jpa-2.0 eclipselink criteria-api

对于具有多列ID的实体,我们需要在给定列表中保留DB中尚未存在的所有这些对象。由于要检查的对象数量很大,并且存储中的对象数量可能非常大,因此我们的想法是使用多列私钥和“WHERE ... IN(。”来从List中选择现有对象。 ..)“使用条件API构造的类型语句,以便在持久化之前可以从列表中删除它们。

以下是尝试执行此操作的代码:

    public void persistUnique(List<CdrEntity> cdrs) {
        // find all the CDRs in the collection that are already in the DB
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<CdrEntity> query = criteriaBuilder.createQuery(CdrEntity.class);
        Root<CdrEntity> cdrRoot = query.from(CdrEntity.class);
        query.select(cdrRoot).where(cdrRoot.in(cdrs));
        List<CdrEntity> nonUnique = entityManager.createQuery(query).getResultList();
        // remove nonUnique elements from crds and persist
        ...
    }

但是,至少使用EclipseLink 2.4.1,这是实际发送到数据库的内容(某些输出因可读性而被省略):

[EL Fine]: sql:...--SELECT SUBINDEX, ... FROM CDRS WHERE ((?, ?, ?, ?) IN ((?, ?, ?, ?), (?, ?, ?, ?), ...))
bind => [null, null, null, null, 2, 1362400759, 19415, 176, ...]

基本上,在主键列的相应列名称应该是的位置,会添加许多参数并稍后绑定(值为null)。除此之外,查询很好,如果前四个?被实际的列名替换,则执行所需的结果。

然而,似乎直接在.in(...)上调用Root没有获得所需的结果。如果直接使用实体是不可能的,我希望有某种Expression可以代表多个列,然后可以成为调用.in(...)的接收者,但我还没有能够找到任何。

所以,问题是:如何正确地做到这一点?或者JPA根本不可能吗?或者EclipseLink中只有一个错误?

1 个答案:

答案 0 :(得分:2)

有趣的尝试,以及生成的奇怪的SQL,SQL看起来左侧是正确的,但不是正确的。请记录一个错误,这是可以支持的。 (也可以在2.5 dev构建中尝试它,它可能会工作)。

JPA Criteria API不支持嵌套数组,所以这超出了JPA规范。

您可以为左侧的id字段创建路径表达式列表,并为此使用CriteriaBuilder literal()表达式,它可能有用。

JPA标准的方法是在每个对象的id比较的表达式中迭代对象列表和or()。