简体   繁体   English

@OneToMany关系使用单个@JoinColumn?

[英]@OneToMany relationship using a single @JoinColumn?

I have the following tables (most essential columns shown only, A & B are not the real names btw): 我有以下表格(仅显示了最重要的列,A和B并不是真实的名字):

table A {
  ...
}

table B {
  ...
}

table METADATA {
  KEY
  VALUE
  REF_A
  REF_B
}

METADATA holds additional key/value meta data for both table A & B. The key/value is needed as we have to handle dynamic data for which we cannot up front create columns for in A and B. METADATA拥有表A和B的其他键/值元数据。需要键/值,因为我们必须处理无法为A和B预先创建列的动态数据。

The entities are setup as (JPA using hibernate as provider): 实体设置为(使用休眠作为提供程序的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;
  ...
}

This setup works fine. 此设置工作正常。 However we have run into issues on some databases (for instance DB2) with a unique index we create (to ensure a meta key is only used once for a given row in A or B): 但是,在某些我们创建的具有唯一索引的数据库(例如DB2)上遇到了问题(以确保对A或B中的给定行只使用一次元键):

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

as creating the index requires requires that all columns are non-null. 因为创建索引要求所有列都不为空。 This is not the case for use with the above design as the domain logic will either be that the meta data is set on A or B, hence one of these will always be null. 在上述设计中不是这种情况,因为域逻辑要么是将元数据设置在A上,要么是B上设置的,因此其中之一始终为空。

Possible solutions of course are to split the METADATA into two tables, one for A and one for B. However I would prefer to keep one table and instead just have one "REF" column which would either be an A or B as well as a TYPE column to say whether it's a meta data for an A or B. The TYPE would be needed as we have separate sequences for id for each table and a A and B could get the same technical id and hence get mixed up data otherwise. 当然,可能的解决方案是将METADATA分为两个表,一个用于A,一个用于B。但是,我宁愿保留一个表,而只保留一个“ REF”列,该列既可以是A或B,也可以是TYPE列说明它是A还是B的元数据。由于我们对每个表都有单独的ID序列,因此A和B可能会获得相同的技术ID,因此会混淆数据,因此需要TYPE。

My question is - is there any way to set this up with JPA? 我的问题是-是否可以通过JPA进行设置?

For one-table based inheritance there is a @DiscriminatorValue which can be used to distinguish the specific stored sub-class, can this be used here as well? 对于基于单表的继承,有一个@DiscriminatorValue可用于区分特定的存储子类,也可以在这里使用它吗? I am looking for something like: 我正在寻找类似的东西:

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

so basically when a meta data is inserted for A this SQL would be used: 因此,基本上,当为A插入元数据时,将使用以下SQL:

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

Any suggestion are welcomed. 任何建议都欢迎。

Rgs, 角色扮演,

-Martin -马丁

I'm not sure why you need to create a key (metakey) in the metadata table, given thet the rows are already tied either to Table A or Table B. 我不确定为什么需要在元数据表中创建键(麦迪),因为这些行已经绑定到表A或表B。

However I think the problem is in considering the MetaData table an entity, given that its only purpose is to save some extra information of an existing entity, it means that you cannot have a row in MetaData without a row in TableA or TableB. 但是,我认为问题在于将MetaData表视为一个实体,鉴于其唯一目的是保存现有实体的一些额外信息,这意味着如果没有TableA或TableB中的一行,则无法在MetaData中拥有一行。

Instead of using relationship mapping one option is to use element collections to directly have the Map of key/value pairs in the corresponding entities: 除了使用关系映射之外,一个选择是使用元素集合在相应实体中直接具有键/值对的映射:

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

Note that there is no java class for a "MetaData" table or entity, the table is automatically mapped from the @ElementCollection and @CollectionTable annotations. 请注意,“ MetaData”表或实体没有Java类,该表是从@ElementCollection和@CollectionTable批注自动映射的。

The above mappings correspond to the following MetaData table: 上面的映射对应于以下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    |       |
+-----------+--------------+------+-----+---------+-------+

If you prefer to keep a separate java class for MetaData to keep using a List instead of a Map, it can also be done with @ElementCollection, you just need to annotate the MetaData class with @Embeddable instead of @Entity. 如果您希望为MetaData保留一个单独的Java类以继续使用List而不是Map,则也可以使用@ElementCollection来完成,您只需要使用@Embeddable而不是@Entity来注释MetaData类。 In that way it doesn't need an Id column like a regular entity. 这样,它不需要像常规实体那样的Id列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM