5

Utilizziamo l'ereditarietà di una tabella per ogni tabella nella nostra applicazione. Ciò consente a diverse istanze dello stesso stack di applicazioni di funzionare con gli stessi DAO mentre le loro entità potrebbero differire leggermente potenzialmente potenzialmente contenenti informazioni univoche per tale istanza. Una classe astratta definisce la struttura della tabella di base e un'estensione definisce colonne aggiuntive, se richiesto, da quella istanza:Posso rimuovere la colonna discriminator in un'ereditarietà della tabella singola di Hibernate?

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

applicazione A:

@Entity 
public class ClientSimple extends Client { 
    private String name; 
    // getter, setter 
} 

applicazione B:

@Entity 
public class ClientAdvanced extends Client { 
    private String description; 
    // getter, setter 
} 

Ora un DAO può funzionare con gli oggetti Client per le applicazioni A e B, ma l'applicazione B può definire informazioni aggiuntive per il proprio oggetto client che possono essere letti da un metodo di gestione uniqu e per l'applicazione B:

applicazione A:

Client client = new ClientSimple(); 
clientDao.save(client); 

applicazione B:

Client client = new ClientAdvanced(); 
clientDao.save(client); 

Purtroppo questo significa che c'è una colonna DTYPE in ogni tabella (o qualsiasi altro nome che io possa scegliere) . C'è un modo per sbarazzarsi di questo? Non ne abbiamo bisogno e sta utilizzando lo spazio DB ...

Grazie!


EDIT

Importante notare: @MappedSuperclass non funzionerà. Stiamo utilizzando QueryDSL come il nostro livello di astrazione HQL. Ciò richiede classi di tipo di query generate automaticamente per il tipo di query di salvataggio. Questi tuttavia verranno generati correttamente solo se la classe astratta è annotata con @Entity.

Questo è neccessairy perché vogliamo interrogare contro la classe astratta Client mentre in realtà l'interrogazione ClientSimple in applicazione A e ClientAdvanced in applicazione B:

Quindi, in qualsiasi applicazione che questo funzionerà:

query.where(QClient.client.name.equals("something"); 

ed in applicazione B questo lavoro:

query.where(QClientSimple.client.description.equals("something else"); 

EDIT2 - riducono

E sembra ridursi a questo: Posso configurare ibernazione in fase di deploy per impostare il tipo discriminatore per un'entità inhertited ad un valore fisso. Quindi, con il mio esempio uno Client sarà sempre ClientSimple in un'applicazione e ClientAdvanced nell'altro in modo che non debba memorizzare tali informazioni nel database?

Come ho detto: Ogni applicazione sarà un'istanza dello stack dell'applicazione di base. Ogni applicazione potrebbe definire colonne aggiuntive per il proprio database locale, ma TUTTI gli oggetti saranno dello stesso tipo per quell'istanza, quindi garantiamo che il discriminatore è sempre lo stesso, rendendolo ridondante nel database e un caso d'uso per la configurazione di ibernazione.

+0

Quale versione di Querydsl stai utilizzando? –

+0

Utilizziamo QueryDSL 2.2.3 ma è possibile aggiornare se le versioni più recenti supportano le superclassi mappate come target di query: come segue: '@MappedSuperclass @Table public abstract class Client ...' + '@Entity public class ClientSimple estende Client' == > genera tipi di query ... ==> query: 'QClient.client.name' – Pete

+1

Non è supportato direttamente, ma è possibile aggiungere un ticket per questo su GitHub. È facilmente implementabile. –

risposta

1

Se mai bisogno di utilizzare sia ClientSimple e ClientAdvanced nella stessa applicazione è possibile dichiarare Client come @MappedSuperclass piuttosto che @Entity.

+0

Purtroppo non è possibile. Stiamo usando query DSL come nostro livello di astrazione HQL. Ciò richiede che le classi astratte siano annotate @Entity in modo che una query possa essere eseguita contro la classe astratta mentre di fatto sta interrogando l'estensione. Modificherò il mio post per riflettere questa limitazione. – Pete

1

In Hibernate, la gerarchia Tabella singola per classe richiede sempre una colonna discriminante per distinguere tra le entità in quanto tutte le classi in una gerarchia sono memorizzate in una tabella. Ecco un esempio di Hibernate Single Table per Class Hierarchy.

Ma si può prendere in considerazione un diverso schema di gerarchia come di seguito:

Hibernate Single Table per Subclass

Vantaggi

  • Utilizzando questa gerarchia, non richiede complesse modifiche al database schema quando viene modificata una singola classe genitore.
  • Funziona bene con gerarchia superficiale.

Svantaggi

  • Come la gerarchia cresce, può compromettere le prestazioni.
  • Anche il numero di join necessari per costruire una sottoclasse aumenta.

Hibernate Single Table per Concrete class

Vantaggi

  • Questo è il metodo più semplice di mappatura eredità da implementare.

Svantaggi

  • questo è dati appartiene a una classe genitore è sparsa in un certo numero di tavoli sottoclasse, che rappresenta classi concrete.
  • Questa gerarchia non è consigliata per la maggior parte dei casi.
  • modifiche a una classe padre si riflette sul gran numero di tavoli
  • Una query formulata in termini di classe genitore è probabile che a causare un gran numero di operazioni di selezione

io vi suggerisco di avere un guarda lo schema Tabella singola per sottoclasse.Anche se non sono sicuro del tuo esatto requisito. Ma questo può aiutare.

+0

Sei assolutamente sicuro che non ci sia modo di scrivere un deserializzatore personalizzato? Non sono un esperto di ibernazione, ma scommetto che se guardassimo il modo in cui Hibernate converte il ResultSet in oggetto, c'è un modo per personalizzarlo come vuole Pete. –

+0

Grazie per i link viralpatel, buona lettura ma sfortunatamente non aiuta molto. Forse questo non può essere fatto solo con annotazioni, come i suggerimenti di un ragazzo mograbi ...?! – Pete

+0

Ciao Guy, beh, è ​​certo che puoi sovrascrivere molte funzionalità predefinite di framework aperti come Hibernate. Ma il problema con questo (tabella per gerarchia) è che i tuoi dati sono memorizzati in una singola tabella. E deve esserci una colonna per identificare quale riga di dati appartiene a quale Classe nella gerarchia. –

7

Lo so, questa è una domanda molto vecchia, ma ho riscontrato questo problema recentemente e questo potrebbe rivelarsi utile a qualcuno.

Questa operazione può essere eseguita utilizzando l'annotazione di Hibernate @DiscriminatorFormula. La seguente descrizione si basa sul libro Java Persistence with Hibernate, sezione 5.1.3; la parte rilevante inizia pagina l'ultimo paragrafo a pagina 202.

Con @DiscriminatorFormula è possibile fornire una SQL dichiarazione che determina il valore della discriminator durante il recupero delle righe rilevanti dal database. Nel tuo caso, dovrebbe essere una semplice stringa che valuta un valore arbitrariamente selezionato. Affinché ciò funzioni, è necessario decidere un nome da utilizzare per l'entità Client. Supponiamo di selezionare "GenericClient" come nome di entity. Questo è il nome che dovrebbe apparire all'interno dell'annotazione @Entity come valore dell'attributo name. Quindi, l'esempio completo, nel tuo caso sarebbe simile al seguente.

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
@DiscriminatorFormula("'GenericClient'") // *1* 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

// Application A 
@Entity 
@DiscriminatorValue("GenericClient") // *2* 
public class SimpleClient extends Client { 
    // ... 
} 


// Application B 
@Entity 
@DiscriminatorValue("GenericClient") // *3* 
public class AdvancedClient extends Client { 
    // ... 
} 

La linea che viene indicato con '' è una parte del frammento SQL che restituisce sempre 'GenericClient' come valore. Il dello Client deve sempre essere annotato con lo @DiscriminatorValue("GenericClient"). Ciò significa che quando Hibernate recupera le righe dal DB, il tipo dell'oggetto da costruire sarebbe sempre la sottoclasse specifica di Client.

Se il pacchetto in cui le sottoclassi di Client risiedono, e il nome delle sottoclassi sono fissi: non sarebbe necessario

In tal caso, il @DiscriminatorValue("GenericClient") sulle sottoclassi, tutto il necessario fare è:

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
@DiscriminatorFormula("'com.example.fixed.path.FixedSubClassName'") 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

Le sottoclassi non avrebbero bisogno di annotazioni. Il valore discriminator-value predefinito è entity-name, che per impostazione predefinita è il nome di classe completo.

Nota: Il SQL istruzione all'interno @DiscriminatorFormula() può essere qualsiasi SQL dichiarazione valida per il server DB mirato.