我有以下表格(仅显示最重要的列,A& B不是实名btw):
table A {
...
}
table B {
...
}
table METADATA {
KEY
VALUE
REF_A
REF_B
}
METADATA为表A和表A保存了额外的键/值元数据。 B.需要键/值,因为我们必须处理动态数据,我们无法事先为A和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)上遇到了一些问题,我们创建了一个唯一的索引(以确保元键只对A或B中的给定行使用一次):
CREATE UNIQUE INDEX METADATA_UNIQUE_KEY ON METADATA (METAKEY, REF_A, REF_B)
因为创建索引要求所有列都是非空的。这不适用于上述设计,因为域逻辑将是元数据在A或B上设置,因此其中一个将始终为空。
当然可能的解决方案是将METADATA分成两个表,一个用于A,一个用于B.但是我宁愿保留一个表,而只是有一个“REF”列,它可以是A或B以及TYPE列,它是否是A或B的元数据。由于我们为每个表都有单独的id序列,因此需要TYPE,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
答案 0 :(得分:1)
我不确定为什么你需要在元数据表中创建一个密钥(metakey),因为这些行已经与表A或表B相关联。
但是我认为问题在于将MetaData表视为一个实体,因为它的唯一目的是保存现有实体的一些额外信息,这意味着在TableA或TableB中没有行的MetaData中不能有一行
不使用关系映射,而是使用元素集合直接在相应的实体中使用键/值对的映射:
@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;
}
请注意,&#34; MetaData&#34;没有java类。表或实体,表从@ElementCollection和@CollectionTable注释自动映射。
以上映射对应于以下MetaData表:
+-----------+--------------+------+-----+---------+-------+
| 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 | |
+-----------+--------------+------+-----+---------+-------+
如果您希望为MetaData保留一个单独的java类来继续使用List而不是Map,那么也可以使用@ElementCollection来完成,您只需要使用@Embeddable而不是@Entity来注释MetaData类。通过这种方式,它不像常规实体那样需要Id列。