2009-09-29 8 views
11

Ich habe eine Situation, in der ich Tabellen für ein Objekt in einer ORM-Klassenhierarchie verbinden muss, wobei die Join-Spalte NICHT der Primärschlüssel der Basisklasse ist. Hier ist ein Beispiel der Tabelle Design:Wie verknüpfe ich Tabellen in Nicht-Primärschlüsselspalten in Sekundärtabellen?

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) 
) 

Und hier sind die Abbildungen (Getter und Setter für Kürze beseitigt

@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; 
}  

Wie kann ich die Zuordnung für BAR_NAMES gegeben hinzufügen, dass seine Join-Spalte nicht FOO_ID, aber BAR_ID

ich folgendes versucht:?

@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>(); 

Dies schlägt fehl, da das SQL zum Abrufen des Bar-Objekts versucht, einen BAR_ID-Wert aus der FOO-Tabelle abzurufen. Ich habe auch versucht mit

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

Dies erzeugt keine SQL-Fehler die JoinTable Anmerkung zu ersetzen, sondern ruft auch keine Daten, da die Abfrage gegen BAR_NAMES wird die FOO_ID als Wert beitreten anstelle des BAR_ID.

Zu Testzwecken habe ich

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'); 

Viele Lösungen, die die DB mit den folgenden Befehlen bevölkert, die eine leere Sammlung zu arbeiten erscheint zurück, wenn für ID Foo Objekt bekommen 10 (im Gegensatz zu einer Sammlung enthalten im Gegensatz 1 Name)

Antwort

6

Ich konnte eine Lösung dafür finden. Wenn Sie die Bar Klassenmap wie so

@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; 
} 

und fügen Sie die folgende Klasse

@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; 
} 

können Sie fügen dann jede Art von Mapping Sie auf die MiniBar Klasse wollen, als ob BarID der Primärschlüssel waren, und dann stellen Sie es weiter in der äußeren Bar Klasse zur Verfügung.

+0

'barId' ist immer noch mit' @ Id' markiert, was ist der Unterschied? – deathangel908

2

nicht sicher, wie dies mit JPA/Erläuterungen zu tun, sondern mit Hibernate XML-Mapping-Dateien wäre es so etwas wie:

<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

Ich glaube, das ist äquivalent zu @JoinTable (name = "BAR_NAMES", joinColumns = @ JoinColumn (name = "BAR_ID")), die nicht funktioniert, da es den Wert von FOO_ID verwendet, um die Tabelle BAR_NAMES beizutreten. – Jherico

+0

Entschuldigung, ich habe falsch gelesen, was du gesagt hast. Ich kann die BAR_ID nicht als Primärschlüssel für die Bar-Klasse zuordnen, da es ein untergeordnetes Objekt des Foo-Objekts ist, das entweder die Vererbung einzelner Tabellen mit sekundären Tabellen oder die Verwendung der Vererbung von verknüpften Unterklassen verwendet. – Jherico

2

Sie werden nicht in der Lage sein zu tun, was Sie wollen. @CollectionOfElements (und @OneToMany, für diese Angelegenheit) sind immer zugeordnet über den Primärschlüssel des Besitzers.

Die Art, wie Sie die Foo/Bar-Vererbung zuordnen, ist auch ziemlich seltsam - sie sind eindeutig nicht in der gleichen Tabelle; scheint wie mit JoinedSubclass wäre ein besserer Ansatz. Beachten Sie, dass Ihnen die Zuordnung bar_names zu bar_id immer noch nicht hilft, da der Primärschlüsselwert in der Hierarchie geteilt wird (auch wenn der Spaltenname für die Unterklasse unterschiedlich sein kann).

Eine mögliche Alternative ist die Verwendung von @OneToOne Mapping zwischen Foo und Bar anstelle der Vererbung. Nur so können Sie bar_names bis bar_id und die am besten geeignete Zuordnung für Ihre Tabellenstruktur zuordnen (allerdings nicht für Ihr Domänenmodell).

+0

In der Live-Umgebung ist Foo eine Basisklasse für viele Unterklassen mit unterschiedlichen Typ-IDs. Wir haben sowohl das beigefügte Unterklassenmodell als auch das Einzelklassenmodell mit Sekundärtabellen verwendet und beide haben ihre Stärken und Schwächen. Das Ändern der Hierarchie ist keine Option. – Jherico

+0

Um ehrlich zu sein, sobald Sie den Ansatz "Tabelle-pro-Hierarchie" mit sekundären Tabellen erweitern, verlieren Sie den einzigen Vorteil, den es hat (etwas schnellere Leistung) im Vergleich zu "Tabelle-pro-Klasse" Ansatz, aber das ist nicht relevant für Ihre Frage. Wie gesagt, Sie können Ihre Sammlung nicht so abbilden, wie Sie wollen, ohne entweder die Hierarchiezuordnung oder die Datenbankstruktur zu ändern (z. B. für 'bar_id' zu verzichten und' foo_id' als PK in der gesamten Hierarchie zu verwenden). – ChssPly76

+0

Der referredColumnName ist speziell für diese Art von Problem gedacht. Wenn ich eine Tabelle TYPE_NAMES erstelle, die sich an die TYPE_ID der FOO-Tabelle anschließt, funktioniert das wie erwartet. Wenn die Zuordnung für eine Spalte in einer sekundären Tabelle nicht zulässig ist, handelt es sich entweder um einen Fehler oder eine Entwurfsbeschränkung. Ich versuche herauszufinden, was. – Jherico

Verwandte Themen