2013-03-04 206 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