对于具有多列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中存在一个简单的错误?已生成
感谢您的快速响应。不幸的是,使用'CriteriaBuilder.literal()'方法似乎没有办法;事实上,它会产生更糟糕的SQL: – Daniel 2013-03-05 09:38:53
WHERE(((?,?,?,?)IN(?,?,?,?,...)) - 然后继续绑定实际路径和实体这个'Root.in()'方法几乎是正确的,它直观地看起来应该是'Root.in()'应该做的事情,毕竟Root始终代表一个实体,对吧? ,EclipseLink 2.5不是这个项目的选择,因为我们即将发布(只是试图在这里干净地处理重复记录问题),但我会给它一个旋转,看看它是否有一些时间做的伎俩。 – Daniel 2013-03-05 09:49:16
我最终通过迭代列表来解决这个问题,并且为各种PK列添加了一系列包含ANDed比较的ORed表达式,这正是我真正希望避免的。这是非常丑陋的,并产生了我曾经在一个地方见过的最多的括号,但它是有效的。既然你认为这是“standa “方式”,我正在接受你的答案。 – Daniel 2013-03-05 17:57:56