2009-09-29 10 views
11

Ho una situazione in cui ho bisogno di unire tabelle su un oggetto in una gerarchia di classi ORM in cui la colonna di join NON è la chiave primaria della classe base. Ecco un esempio della struttura della tabella:Come faccio a unire le tabelle su colonne chiave non primarie nelle tabelle secondarie?

CREATE TABLE APP.FOO 
(
    FOO_ID INTEGER NOT NULL, 
    TYPE_ID INTEGER NOT NULL, 
    PRIMARY KEY(FOO_ID) 
) 

CREATE TABLE APP.BAR 
(
    FOO_ID INTEGER NOT NULL, 
    BAR_ID INTEGER NOT NULL, 
    PRIMARY KEY(BAR_ID), 
    CONSTRAINT bar_fk FOREIGN KEY(FOO_ID) REFERENCES APP.FOO(FOO_ID) 
) 

CREATE TABLE APP.BAR_NAMES 
(
    BAR_ID INTEGER NOT NULL, 
    BAR_NAME VARCHAR(128) NOT NULL, 
    PRIMARY KEY(BAR_ID, BAR_NAME), 
    CONSTRAINT bar_names_fk FOREIGN KEY(BAR_ID) REFERENCES APP.BAR(BAR_ID) 
) 

Ed ecco le mappature (getter e setter eliminati per brevità

@Entity 
@Table(name = "FOO") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "TYPE_ID", discriminatorType = javax.persistence.DiscriminatorType.INTEGER) 
public abstract class Foo { 
    @Id 
    @Column(name = "FOO_ID") 
    private Long fooId; 
} 

@Entity 
@DiscriminatorValue("1") 
@SecondaryTable(name = "BAR", pkJoinColumns = { @PrimaryKeyJoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID") }) 
public class Bar extends Foo{ 
    @Column(table = "BAR", name = "BAR_ID") 
    Long barId; 
}  

Come si aggiunge la mappatura per BAR_NAMES dato che la sua colonna di join non è FOO_ID, ma BAR_ID

ho provato quanto segue:?

@CollectionOfElements(fetch = FetchType.LAZY) 
@Column(name = "BAR_NAME") 
@JoinTable(name = "BAR_NAMES", joinColumns = @JoinColumn(table = "BAR", name = "BAR_ID", referencedColumnName="BAR_ID")) 
List<String> names = new ArrayList<String>(); 

Questo fallisce perché SQL per recuperare l'oggetto barra cerca di ottenere un valore BAR_ID dalla tabella FOO. Ho anche provato a sostituire l'annotazione JoinTable con

@JoinTable(name = "BAR_NAMES", joinColumns = @JoinColumn(name = "BAR_ID")) 

Questo produce alcun errore SQL, ma recupera anche dati perché la query BAR_NAMES utilizza il FOO_ID come valore join anziché sul BAR_ID.

A scopo di verifica, ho popolato il DB con i seguenti comandi

insert into FOO (FOO_ID, TYPE_ID) values (10, 1); 
insert into BAR (FOO_ID, BAR_ID) values (10, 20); 
insert into BAR_NAMES (BAR_ID, BAR_NAME) values (20, 'HELLO'); 

Molte soluzioni che sembrano lavorare restituirà un insieme vuoto quando sempre oggetto Foo per ID 10 (al contrario di una raccolta contenente 1 nome)

risposta

6

sono stato in grado di trovare una soluzione a questo. Se si mappa la classe Bar in questo modo

@Entity 
@DiscriminatorValue("1") 
@SecondaryTable(name = "BAR", pkJoinColumns = { @PrimaryKeyJoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID") }) 
public class Bar extends Foo { 
    @OneToOne 
    @JoinColumn(table = "BAR", name = "BAR_ID") 
    MiniBar miniBar; 
} 

e aggiungere la seguente classe

@Entity 
@SqlResultSetMapping(name = "compositekey", entities = @EntityResult(entityClass = MiniBar.class, fields = { @FieldResult(name = "miniBar", column = "BAR_ID"), })) 
@NamedNativeQuery(name = "compositekey", query = "select BAR_ID from BAR", resultSetMapping = "compositekey") 
@Table(name = "BAR") 
public class MiniBar { 
    @Id 
    @Column(name = "BAR_ID") 
    Long barId; 
} 

È quindi possibile aggiungere qualsiasi tipo di corrispondenza che si desidera la classe MiniBar come se Bared fosse la chiave primaria, e quindi renderlo ulteriormente disponibile nella classe Bar esterna.

+0

'barId' è ancora contrassegnato con' @ Id', qual è la differenza? – deathangel908

2

Non sai come farlo con JPA/annotazioni, ma con file di mapping XML Hibernate sarebbe qualcosa di simile:

<class name="Bar" table="BAR"> 
    <id name="id" type="int"> 
     <column name="BAR_ID"/> 
     <generator class="native"/> 
    </id> 
    <set name="barNames" table="BAR_NAMES"> 
     <!-- Key in BAR_NAMES table to map to this class's key --> 
     <key column="BAR_ID"/> 
     <!-- The value in the BAR_NAMES table we want to populate this set with --> 
     <element type="string" column="BAR_NAME"/> 
    </set> 
</class> 
+0

Credo che questo sia equivalente a @JoinTable (name = "BAR_NAMES", joinColumns = @JoinColumn (name = "BAR_ID")), che non funziona perché utilizza il valore di FOO_ID per unirsi alla tabella BAR_NAMES. – Jherico

+0

Scusa, ho letto male ciò che stavi dicendo. Non riesco a mappare BAR_ID come chiave primaria per la classe Bar perché è figlio dell''oggetto Foo che utilizza l'ereditarietà di una singola tabella con tabelle secondarie o l'ereditarietà della sottoclasse unita. – Jherico

2

Non sarai in grado di fare quello che vuoi. @CollectionOfElements (e @OneToMany, per quella materia) sono sempre mappato tramite chiave di entità primaria del proprietario.

Il modo in cui si esegue il mapping Foo/Bar eredità è piuttosto strano, come pure - che chiaramente non sono nella stessa tabella; sembra che usare JoinedSubclass sia un approccio migliore. Tenete a mente che ancora non vi aiuterà a mappare bar_names-bar_id perché il valore della chiave primaria è condivisa tra la gerarchia (anche se il nome della colonna può essere diverso per sottoclasse).

Una possibile alternativa è quella di utilizzare @OneToOne mappatura tra Foo e bar invece di eredità. Questo è l'unico modo per cui è possibile mappare bar_names a bar_id e il mapping più appropriato per la struttura della tabella (sebbene, forse, non per il modello di dominio).

+0

Nell'ambiente live Foo è una classe base per molte sottoclassi con ID di tipo diverso. Abbiamo utilizzato sia il modello di sottoclasse unito che il modello a classe singola con tabelle secondarie e abbiamo trovato entrambi i punti di forza e di debolezza. La modifica della gerarchia non è un'opzione. – Jherico

+0

Per essere onesti, una volta aumentato l'approccio "tabella per gerarchia" con tabelle secondarie, perdi l'unico vantaggio che ha (prestazioni un po 'più veloce) rispetto all'approccio "tabella per classe" ma non è pertinente alla tua domanda. Come ho già detto, non sarai in grado di mappare la tua collezione nel modo desiderato senza modificare la mappatura della gerarchia o la struttura del tuo database (ad esempio, abbandonando 'bar_id' e usando' foo_id' come PK in tutta la gerarchia). – ChssPly76

+0

Il referencedColumnName è specificamente lì per risolvere questo tipo di problema. Se creo una tabella TYPE_NAMES che si collega al TYPE_ID della tabella FOO, funziona come previsto. La mancata abilitazione del mapping su una colonna in una tabella secondaria è un bug o una limitazione di progettazione. Sto cercando di determinare quale. – Jherico

Problemi correlati