2013-04-25 83 views
0

我有以下表(最重要的列僅示出,A & B不實名順便說一句):@OneToMany關係使用一個@JoinColumn?

table A { 
    ... 
} 

table B { 
    ... 
} 

table METADATA { 
    KEY 
    VALUE 
    REF_A 
    REF_B 
} 

METADATA保持額外的鍵/值元數據兩者表阿& B.鍵/需要值,因爲我們必須處理一個我們不能在前面所述的創建列和B.

的實體是設置爲(JPA採用Hibernate提供商)動態數據:

interface Entity { 
    ... 
    getId() 
    ... 
} 

class A implements Entity { 
    ... 
    @OneToMany(cascade = {ALL}, mappedBy = "a", orphanRemoval = true, fetch = LAZY) 
    private List<MetaData> metaData; 
    ... 
    @Override 
    public List<MetaData> getMetaData() { 
    return metaData; 
    } 
    ... 
} 

class B implements Entity { 
    ... 
    @OneToMany(cascade = {ALL}, mappedBy = "b", orphanRemoval = true, fetch = LAZY) 
    private List<MetaData> metaData; 
    ... 
    @Override 
    public List<MetaData> getMetaData() { 
    return metaData; 
    } 
    ... 
} 

class MetaData implements Entity { 
    ... 
    @ManyToOne 
    @JoinColumn(name = "REF_A", nullable = true) 
    private A a; 

    @ManyToOne 
    @JoinColumn(name = "REF_B", nullable = true) 
    private B b; 
    ... 
} 

這個設置工作正常。然而,我們遇到了與我們創建唯一索引在某些數據庫的問題(例如DB2)(確保Meta鍵只在A或B使用一次對於一個給定行):

CREATE UNIQUE INDEX METADATA_UNIQUE_KEY ON METADATA (METAKEY, REF_A, REF_B) 

爲創建索引要求要求所有列都是非空的。這是不適合用上述設計的情況下使用作爲域邏輯將是一個在元數據被設定在A或B,因此,其中之一將總是爲空。

當然可能的解決方案是將元數據分成兩個表,一個是和一個B.但是我寧願保持一個表,而不是僅僅有一個「REF」列其要麼是A或B還有一個類型列說無論是對於A或B.將需要的類型,我們有一個ID爲每個表和A和B單獨序列可以得到相同的技術ID,因此被弄混元數據否則數據。

我的問題是 - 有什麼辦法可以使用JPA設置呢?

對於基於一個表繼承有可用於區分特定儲存的子類中,可以這樣這裏也用到了一個@DiscriminatorValue?我在尋找的東西,如:

table A { 
    ... 
} 

table B { 
    ... 
} 

table METADATA { 
    KEY 
    VALUE 
    REF 
    TYPE 
} 


@DiscriminatorValue("A") 
class A implements Entity { 
    ... 
    @OneToMany(cascade = {ALL}, mappedBy = "entity", orphanRemoval = true, fetch = LAZY) 
    private List<MetaData> metaData; 
    ... 
    @Override 
    public List<MetaData> getMetaData() { 
    return metaData; 
    } 
    ... 
} 

@DiscriminatorValue("B") 
class B implements Entity { 
    ... 
    @OneToMany(cascade = {ALL}, mappedBy = "entity", orphanRemoval = true, fetch = LAZY) 
    private List<MetaData> metaData; 
    ... 
    @Override 
    public List<MetaData> getMetaData() { 
    return metaData; 
    } 
    ... 
} 

class MetaData implements Entity { 
    ... 
    @ManyToOne 
    @JoinColumn(name = "REF", nullable = true) 
    private Entity entity; 

    @DiscriminatorColumn(name="TYPE", discriminatorType=STRING, length=20)  
    private String type; 
    ... 
} 

所以基本上時元數據被插入A將被用於這個SQL:

INSERT INTO METADATA (KEY, VALUE, REF, TYPE) VALUES ("metaKey", "metaValue", 1, "A") 

任何建議都歡迎。

RGS,

-Martin

回答

1

我不知道爲什麼你需要創建元數據表中的鍵(metakey),給泰德行已經綁要麼表A或表B

但是我認爲問題在於考慮MetaData表的一個實體,因爲它的唯一目的是保存現有實體的一些額外信息,這意味着你不能在表中沒有行的MetaData中有一行或TableB。

而不是使用關係映射一個選項是使用元素的集合直接有地圖鍵/值對在相應的實體:

@Entity 
@Table(name="TableA") 
public class TableA 
{ 
    @Id 
    @GeneratedValue(strategy= GenerationType.TABLE) 
    private int id; 

    @ElementCollection 
    @CollectionTable(name="MetaData", joinColumns={@JoinColumn(name="TableA_id")}) 
    @MapKeyColumn(name="metaKey") 
    @Column(name="metaValue") 
    private Map<String, String> metadata; 
} 

@Entity 
@Table(name="TableB") 
public class TableB 
{ 
    @Id 
    @GeneratedValue(strategy= GenerationType.TABLE) 
    private int id; 

    @ElementCollection 
    @CollectionTable(name="MetaData", joinColumns={@JoinColumn(name="TableB_id")}) 
    @MapKeyColumn(name="metaKey") 
    @Column(name="metaValue") 
    private Map<String, String> metadata; 
} 

請注意,有一個「元數據」沒有java類表或實體,該表會自動從@ElementCollection和@CollectionTable註釋映射。

上述映射對應於下列元數據表:

+-----------+--------------+------+-----+---------+-------+ 
| Field  | Type   | Null | Key | Default | Extra | 
+-----------+--------------+------+-----+---------+-------+ 
| TableA_id | int(11)  | YES | MUL | NULL |  | 
| metaValue | varchar(255) | YES |  | NULL |  | 
| metaKey | varchar(255) | YES |  | NULL |  | 
| TableB_id | int(11)  | YES | MUL | NULL |  | 
+-----------+--------------+------+-----+---------+-------+ 

如果您希望保留用於元數據的單獨的Java類使用列表,而不是地圖,以保持,它也可以用做@ElementCollection ,你只需要用@Embeddable而不是@Entity來註釋MetaData類。這樣就不需要像常規實體那樣的Id列。

+0

謝謝你的詳細和有用的答案。這比將元數據作爲一個實體進行處理確實而且更簡單。 但是,它也遭受了我們遇到的原始問題,它具有唯一的約束,以避免給定的A或B具有重複的「metaKey」。在DB2上將不可能添加以下索引: CREATE INDEX METADATA_UNIQUE_KEY ON METALLATA(METAKEY,TableA_id) 作爲「TableA_id」可爲空。 – lanzlord 2013-04-25 23:37:28

+0

對不起,應該是: CREATE UNIQUE INDEX METADATA_UNIQUE_KEY ON METADATA(METAKEY,TableA_id) – lanzlord 2013-04-25 23:43:54

+0

我想我理解有關唯一metaKey的問題。它是否只需要在同一個表(A或B)中唯一或唯一,包括兩個表?如果是第一種情況,可以使用具有Map 的解決方案。 java中的地圖不允許重複的鍵(當你寫第二個值時它只是覆蓋第一個值),所以不需要在數據庫級執行它。 – German 2013-04-26 14:13:57