2013-04-25 132 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