2010-09-03 23 views
10

JPA 2 dispone di un meccanismo per l'esecuzione di query ricorsive?Query JPA ricorsiva?

Ecco la mia situazione: Ho un'entità E, che contiene un campo intero x. Potrebbe anche avere figli di tipo E, mappati tramite @OneToMany. Quello che mi piacerebbe fare è trovare una E per chiave primaria, e ottenere il suo valore di x, insieme ai valori x di tutti i suoi discendenti. C'è un modo per farlo in una singola query?

Sto utilizzando Hibernate 3.5.3, ma preferirei non avere alcuna dipendenza esplicita sulle API di Hibernate.


EDIT: Secondo this voce, Hibernate fa non hanno questa caratteristica, o almeno non ha fatto a marzo. Quindi sembra improbabile che l'APP possa farlo, ma mi piacerebbe esserne sicuro.

risposta

21

L'utilizzo del modello di adiacenza dove ogni riga contiene un riferimento ai relativi genitori che farà riferimento a un'altra riga nella stessa tabella non collabora bene con JPA. Questo perché JPA non ha il supporto per la generazione di query utilizzando la clausola Oracle CONNECT BY o l'istruzione SQL standard WITH. Senza una di queste due clausole non è davvero possibile rendere utile il modello di adiacenza.

Tuttavia, ci sono un paio di altri approcci per modellare questo problema che può essere applicato a questo problema. Il primo è il modello di percorso materializzato . È qui che il percorso completo del nodo viene appiattito in una singola colonna. La definizione della tabella si estende in questo modo:

CREATE TABLE node (id INTEGER, 
        path VARCHAR, 
        parent_id INTEGER REFERENCES node(id)); 

Per inserire un albero di nodi sembra qualche cosa come:

INSERT INTO node VALUES (1, '1', NULL); -- Root Node 
INSERT INTO node VALUES (2, '1.2', 1); -- 1st Child of '1' 
INSERT INTO node VALUES (3, '1.3', 1); -- 2nd Child of '1' 
INSERT INTO node VALUES (4, '1.3.4', 3); -- Child of '3' 

Quindi, per ottenere Nodo '1' e tutti i suoi figli la query è:

SELECT * FROM node WHERE id = 1 OR path LIKE '1.%'; 

Per mappare questo a JPA basta rendere la colonna 'percorso' un attributo del proprio oggetto persistente. Dovrai comunque tenere la contabilità per mantenere aggiornato il campo del "percorso". JPA/Hibernate non lo farà per te. Per esempio. se si sposta il nodo su un genitore diverso, sarà necessario aggiornare sia il riferimento principale che il nuovo valore del percorso dal nuovo oggetto principale.

L'altro approccio è denominato nidificato modello di serie, che è un po 'più complesso. Probabilmente il migliore described dal suo creatore (piuttosto che aggiunto per intero da me).

C'è un terzo approccio chiamato Modello intervallo nidificato, tuttavia questo ha un forte affidamento sulle stored procedure da implementare.

Una spiegazione molto più completa di questo problema è descritta nel capitolo 7 di The Art of SQL.

+0

Questo è tutto piuttosto più complesso di quello che stavo cercando, ma grazie per la risposta informativa. C'è anche un'alternativa più semplice al modello di percorso materializzato: ogni nodo memorizza l'ID del nodo radice dell'albero a cui appartiene. Ciò significa che puoi utilizzare il nodo radice solo come punto di partenza della query, ma nel mio caso specifico non è un problema, quindi è probabilmente quello che farò. –

+0

È possibile utilizzare elenchi di adiacenze, gestiti tramite JPA, ma utilizzare una query nativa per utilizzare le funzionalità SQL ricorsive. Sarebbe un po 'icky, e non portatile, ma sarebbe una sorta di migliore dei due mondi. Gli insiemi nidificati sono portatili, ma laboriosi e non si comportano molto bene per alcuni tipi di query abbastanza comuni. –

+0

È cambiato qualcosa con JPA 2.0 ... Ad esempio, ho usato un Native NamedQuery per avere una clausola 'CONNECT BY' nella mia query. L'ho provato e restituisce i bambini, ma non in una lista gerarchica ... Solo bambini piatti. Qualche idea? – SoftwareSavant

4

La migliore risposta in questo post mi sembra un enorme compromesso. Ho già avuto a che fare con modelli di dati in cui i geniali ingegneri hanno deciso che sarebbe stata una buona idea codificare gli Hiarchies degli alberi nei campi DB come testo come "Europa | Uk | Shop1 | John" e con enormi volumi di dati in queste tabelle . Non sorprendentemente, le prestazioni della query del modulo MyHackedTreeField LIKE 'parentHierharchy%' dove killers. Affrontare questo tipo di problema alla fine richiedeva la creazione nella cache della memoria dei hiearch degli alberi e di tanti altri ...

Se è necessario eseguire una query ricorsiva e il volume dei dati non è enorme ... semplificare la vita e caricare semplicemente i campi DB necessari per eseguire il piano. E codifica la tua ricorsione in java. Non farlo nel DB se non si ha una buona ragione per farlo.

E anche se il volume di dati che hai è enorme, è molto probabile che tu possa suddividere il tuo problema in gruppi di alberi ricorsivi indipenti e processarli uno alla volta senza dover caricare tutti i dati contemporaneamente.