2016-03-30 9 views
10

Breve versione per il frettoloso:approccio migliore per il collegamento di diversi tipi di entità in JPA

C'è varie tabelle/entità nel mio modello di dominio che hanno lo stesso campo (un UUID). C'è una tabella in cui ho bisogno di collegare righe/istanze di tali entità ad altre entità gestite da JPA. In altre parole, l'istanza del campo in quella tabella di collegamento non sarà conosciuta in anticipo. I due approcci mi viene in mente sono:

  • Utilizzare un'entità astratta e una strategia TABLE_PER_CLASS, o
  • utilizzare un @MappedSuperClass negozio il nome della classe dell'istanza nella tabella di collegamento pure, o qualcosa di simile che consente Definisco la logica per ottenere l'istanza effettiva dalla tabella giusta.

Entrambi presentano vantaggi e svantaggi in termini di complessità e prestazioni. Quale credi di essere il migliore, c'è forse una terza opzione, o hai provato qualcosa del genere in passato e consiglierei/avvertire fortemente contro?

Versione lunga nel caso in cui si desidera più di fondo:

Ho un modello di database/oggetto in cui molti tipi hanno un campo comune: un identificatore univoco universale (UUID). La ragione di ciò è che le istanze di questi tipi possono essere soggette a modifiche. Le modifiche seguono il modello di comando e i loro dati possono essere incapsulati e se stessi persistono. Chiamiamo un tale cambiamento come una "mutazione". Deve essere possibile scoprire quali mutazioni esistono nel database per ogni data entità, e viceversa, su quale entità opera una mutazione memorizzata.

Prendere le seguenti entità con UUID come esempio (estremamente semplificato): mutable entities

Per memorizzare le "mutazioni", usiamo un tavolo/un'entità chiamata MutationHolder. Per collegare una mutazione alla sua entità target, c'è un MutationEntityLink. L'unica ragione per cui questo dato non è direttamente sul MutationHolder è perché ci possono essere collegamenti diretti o indiretti, ma questo è di poca importanza qui così ho lasciato fuori: mutation entities

La questione si riduce a come posso modellare il entity campo in MutationEntityLink. Ci sono due approcci a cui riesco a pensare.

Il primo è quello di creare una classe annotata @Entity con il campo UUID. Customer, Contract e Address lo estenderebbero. Quindi è una strategia TABLE_PER_CLASS. Presumo che potrei usare questo come un tipo per il campo entity, anche se non ne sono sicuro. Tuttavia, temo che questo potrebbe avere una grave penalizzazione delle prestazioni poiché JPA avrebbe bisogno di interrogare molte tabelle per trovare l'istanza attuale.

Il secondo è utilizzare semplicemente @MappedSuperClass e memorizzare l'UUID per un'entità nel campo entity di MutationEntityLink. Per ottenere l'entità effettiva con quell'UUID, dovrei risolverlo a livello di codice. L'aggiunta di una colonna aggiuntiva con il nome della classe dell'entità, o qualcos'altro che mi consenta di identificarla o di incollarla in una query JPQL, farebbe. Ciò richiede più lavoro ma sembra più efficiente. Non sono contrario alla codifica di alcune classi di utilità o all'effettuazione di alcune annotazioni/annotazioni personalizzate, se necessario.

La mia domanda è quale di questi approcci sembra migliore? In alternativa, potresti avere un suggerimento migliore o notare che mi manca qualcosa; ad esempio, forse c'è un modo per aggiungere una colonna di tipo anche con l'ereditarietà TABLE_PER_CLASS al punto JPA sulla tabella giusta? Forse hai provato qualcosa di simile e vuoi avvertirmi su numerosi problemi che potrebbero sorgere.

Alcune informazioni aggiuntive:

  • Creiamo lo schema del database, in modo che possiamo aggiungere tutto ciò che vogliamo.
  • Una strategia di ereditarietà a tabella singola non è un'opzione. Le tabelle devono rimanere distinte. Per lo stesso motivo, anche l'ereditarietà unita non sembra una buona idea.
  • Il provider JPA è in modalità di sospensione e l'utilizzo di elementi che non fanno parte dello standard JPA non è un problema.

risposta

6

Se le entità non hanno nulla in comune oltre ad avere un uuid, utilizzerei il secondo approccio che descrivi: usa MappedSuperclass. Rendere la superclasse comune un'entità ti impedirebbe di utilizzare una diversa strategia di ereditarietà se necessario, richiederebbe una tabella per quella super entità anche se non esistono istanze e dal punto di vista del business è semplicemente sbagliato.

Il collegamento stesso potrebbe essere implementato in diversi modi, ad es. è possibile creare una sottoclasse di MutationEntityLink per ogni entità da mappare (es. CustomerMutationEntityLink ecc.) o fare come l'avete descritta, vale a dire memorizzare solo l'uuid e alcune informazioni sul discriminatore/tipo e risolverlo in modo programmatico (stiamo usando tale approccio per qualcosa di simile btw.).

+0

È bello sapere che stai effettivamente utilizzando questo approccio. Mostra che è fattibile. Penso che la sottoclasse di MutationEntityLink per ogni entità mappata sarebbe molto scomoda, quindi una sorta di colonne discriminatorie suona meglio al momento. –

+0

@G_H sì, questo è il motivo per decidere contro le sottoclassi. – Thomas

+1

Mi piace l'idea di @Thomas di creare una sottoclasse di MutationEntityLink per ogni entità. Dal momento che è una classe tecnica che fornisce il collegamento tra le mutazioni e le tue entità, vorrei andare per l'ereditarietà di una singola tabella. La tabella avrebbe la sua colonna discriminatore e una colonna chiave esterna per ogni entità. La tabella potrebbe non essere la rappresentazione più elegante del tuo modello di dominio, ma potresti ottenere buone prestazioni da esso. –

2

È necessario utilizzare @MappedSuperclass mentre si ereditano associazioni/metodi/proprietà mentre TABLE_PER_CLASS viene generalmente utilizzato quando si dispone di entità e sottoentità. Se nel modello ci sono entità che hanno un'associazione con la classe base, utilizzare TABLE_PER_CLASS poiché la classe base si comporta come un'entità. In caso contrario, dal momento che la classe di base dovrebbe includere proprietà/attributi e metodi che sono in generale a tali soggetti non legati gli uni agli altri, usando @MappedSuperclass sarebbe una migliore idea

Esempio 1: È necessario impostare allarmi per alcune attività diverse come "prendi medicine", "chiama mamma", "vai al dottore" ecc. Il contenuto del messaggio di allarme non ha importanza, avrai bisogno di un promemoria. Quindi usa TABLE_PER_CLASS poiché il messaggio di allarme, che è la tua classe base, è come un'entità qui.

Esempio 2: Assumere la classe di base AbstractDomainObject consente di creare ID di accesso, loginName, data di creazione/modifica per ogni oggetto in cui nessun ente ha un'associazione con la classe di base, sarà necessario specificare l'associazione per il bene di compensazione in seguito, come "Azienda", "Università" ecc. In questa situazione, usando @MappedSuperclass sarebbe meglio.

+0

La differenza tra una superclasse mappata e una tabella per strategia di ereditarietà di classe era già chiara per me. La sfida nella mia situazione è che in effetti esiste una tabella che deve fare riferimento a entità che sono implementazioni della classe astratta, ma per il modello di dominio e le prestazioni '@ MappedSuperClass' ha più senso di TABLE_PER_CLASS, quindi per usarlo vorrei ho bisogno di creare io stesso la logica di ricerca per il giusto tipo di entità. La domanda si riduce al fatto che sia fattibile (Thomas conferma così) e se esiste una soluzione/pratica migliore esistente. –

Problemi correlati