2013-03-04 207 views
7

对于具有多列ID的实体,我们需要将给定列表中尚未存在于数据库中的所有对象持久化。由于要检查的对象数量很大,并且存储中的对象数量可能非常大,因此我们的想法是使用其多列私钥和“WHERE ... IN(”)从列表中选择现有对象。 ..)“使用标准API构建的类型语句,以便在持续之前可以从列表中删除它们。如何使用JPA的标准API构造多列“WHERE ... IN”表达式?

下面是试图做到这一点的代码:

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,这是何等的实际发送到DB(一些输出省略掉了可读性):

[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中存在一个简单的错误?已生成

回答

2

有趣的尝试,和奇SQL,则SQL看起来得到了左侧是正确的,但不正确的。请为此记录一个错误,这是可以支持的。 (也可以在2.5开发版本中尝试它,它可能会工作)。

的JPA标准API不支持嵌套的数组,所以任何这已经超出了JPA规范。

您可以创建为左侧ID字段路径表达式的列表,并使用CriteriaBuilder文字()表达这一点,它可能工作。

JPA标准的方法来做到这将是遍历对象列表和或()中每个对象的ID比较的表达式。

+0

感谢您的快速响应。不幸的是,使用'CriteriaBuilder.literal()'方法似乎没有办法;事实上,它会产生更糟糕的SQL: – Daniel 2013-03-05 09:38:53

+0

WHERE(((?,?,?,?)IN(?,?,?,?,...)) - 然后继续绑定实际路径和实体这个'Root.in()'方法几乎是正确的,它直观地看起来应该是'Root.in()'应该做的事情,毕竟Root始终代表一个实体,对吧? ,EclipseLink 2.5不是这个项目的选择,因为我们即将发布(只是试图在这里干净地处理重复记录问题),但我会给它一个旋转,看看它是否有一些时间做的伎俩。 – Daniel 2013-03-05 09:49:16

+0

我最终通过迭代列表来解决这个问题,并且为各种PK列添加了一系列包含ANDed比较的ORed表达式,这正是我真正希望避免的。这是非常丑陋的,并产生了我曾经在一个地方见过的最多的括号,但它是有效的。既然你认为这是“standa “方式”,我正在接受你的答案。 – Daniel 2013-03-05 17:57:56