對於具有多列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