0

我有以下表格(仅显示最重要的列,A 和 B 不是真实姓名顺便说一句):

table A {
  ...
}

table B {
  ...
}

table METADATA {
  KEY
  VALUE
  REF_A
  REF_B
}

METADATA 为表 A 和 B 保存额外的键/值元数据。键/值是必需的,因为我们必须处理无法预先在 A 和 B 中创建列的动态数据。

实体设置为(JPA 使用休眠作为提供者):

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 上,因此其中之一将始终为空。

可能的解决方案当然是将元数据拆分为两个表,一个用于 A,一个用于 B。但是我更愿意保留一个表,而不是只有一个“REF”列,它可以是 A 或 B 以及TYPE 列来说明它是 A 还是 B 的元数据。 TYPE 是必需的,因为我们对每个表都有单独的 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,

-马丁

4

1 回答 1

1

我不确定为什么需要在元数据表中创建一个键(元数据),因为这些行已经绑定到表 A 或表 B。

但是我认为问题在于将 MetaData 表视为一个实体,因为它的唯一目的是保存现有实体的一些额外信息,这意味着如果没有 TableA 或 TableB 中的行,就不能在 MetaData 中有一行。

除了使用关系映射,一种选择是使用元素集合直接在相应实体中拥有键/值对的 Map:

@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 注释自动映射的。

上述映射对应以下 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 列。

于 2013-04-25T21:57:28.577 回答