2013-03-04 8 views
7

Per un'entità con un ID a più colonne, è necessario mantenere tutti gli oggetti in un dato elenco che non sono già presenti nel DB. Poiché il numero di oggetti da controllare è elevato e il numero di oggetti presenti nella memoria può essere estremamente grande, l'idea è di selezionare gli oggetti esistenti dall'elenco utilizzando la loro chiave privata a più colonne e un "DOVE ... IN (. ..) "dichiarazione di tipo costruita utilizzando l'API dei criteri, in modo che possano essere rimossi dall'Elenco prima di persistere.Come si costruiscono le espressioni "WHERE ... IN" a più colonne usando l'API dei criteri di JPA?

Ecco il codice che tenta di fare questo:

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 
     ... 
    } 

Tuttavia, almeno utilizzando EclipseLink 2.4.1, questo è ciò che è in realtà inviato al DB (qualche uscita eliso per migliorare la leggibilità):

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

In sostanza, dove dovrebbero essere i nomi di colonna appropriati per le colonne di chiave primaria, viene aggiunto un numero di parametri e successivamente associato (con il valore null). A parte questo, la query va bene, e se i primi quattro? Sono sostituiti dai nomi delle colonne effettive, viene eseguito con il risultato desiderato.

Sembra tuttavia che chiamare .in(...) direttamente su un Root non abbia il risultato desiderato. Se non è possibile utilizzare direttamente le Entità, mi aspetto che ci sia una specie di Expression che può rappresentare più colonne che potrebbero quindi essere il destinatario di una chiamata a .in(...), ma non sono stato in grado di trovarne.

Quindi, la domanda è: come si fa a farlo correttamente? O non è affatto possibile con JPA? O c'è semplicemente un bug in EclipseLink?

risposta

2

Tentativo interessante e SQL dispari che è stato generato, l'SQL sembra avere il lato sinistro corretto, ma non quello giusto. Si prega di registrare un bug per questo, è qualcosa che potrebbe essere supportato. (provalo anche nella versione di sviluppo 2.5, potrebbe funzionare).

L'API dei criteri JPA non supporta gli array annidati, quindi tutto ciò va oltre le specifiche JPA.

È possibile creare un elenco di espressioni di percorso per i campi id per il lato sinistro e utilizzare l'espressione literal() di CriteriaBuilder per questo, potrebbe funzionare.

Il modo standard JPA per eseguire questa operazione è quello di scorrere l'elenco di oggetti e() in un'espressione per il confronto degli ID di ciascun oggetto.

+0

Grazie per la risposta rapida. Sfortunatamente, l'uso del metodo 'CriteriaBuilder.literal()' non sembra fare il trucco; infatti, produce SQL che è ancora peggio: – Daniel

+0

'WHERE ((((, (?,?,?) IN (?,?,?,?, ...)' - quindi procede a legare il percorso e l'entità effettivi oggetti. Il metodo 'Root.in()' stava quasi facendo la cosa giusta, e intuitivamente sembra che questo dovrebbe essere ciò che 'Root.in()' dovrebbe fare, dopo tutto, un Root rappresenta sempre un'Entità, giusto? , EclipseLink 2.5 non è un'opzione per questo progetto, dato che siamo vicini al rilascio (stiamo solo cercando di risolvere in modo pulito un problema con i record duplicati qui), ma gli darò un giro e vedrò se fa il trucco qualche volta. Se no, inserirò un bug come suggerito – Daniel

+0

Ho finito per risolvere questo iterando sull'elenco e aggiungendo una lunga serie di espressioni ORed contenenti confronti ANDed per le varie colonne PK - esattamente quello che speravo di evitare, davvero. È piuttosto brutto e produce la più grande quantità di parentesi che abbia mai visto in un posto, ma funziona. Dal momento che hai suggerito questa come la "standa" rd way ", sto marcando la tua risposta come accettata. – Daniel

Problemi correlati