来自具有非唯一 ID 的表的 JPA 实体

Posted

技术标签:

【中文标题】来自具有非唯一 ID 的表的 JPA 实体【英文标题】:JPA entity from a table with non-unique id 【发布时间】:2016-03-30 07:34:06 【问题描述】:

我想要一个实体

class InkFormula 
    int id;
    Map<Ink, Integer> inkPercents;        
    ...

来自具有这种结构的单个表:

┌─────┬────────┬─────────┐
│ id  │ ink_id │ percent │
├─────┼────────┼─────────┤
│  1  │   1    │   50    │
├─────┼────────┼─────────┤
│  1  │   2    │   25    │
├─────┼────────┼─────────┤
│  1  │   3    │   0     │
├─────┼────────┼─────────┤
│  2  │   1    │   100   │
├─────┼────────┼─────────┤
│  2  │   2    │   80    │
└─────┴────────┴─────────┘

必须使用非唯一的id 来聚集来自ink_idpercentMap&lt;&gt;

我需要一个实体和一个表的原因是要求具有唯一约束idink_id,以便每个公式不能包含重复的墨水。

JPA 和 Hibernate 可以做到吗?

【问题讨论】:

什么映射到“百分比”? Map 必须从 "ink_id" (int) 和 "percent" (int) 映射 你的意思是这是一个JoinTable? 尼尔,不是。我需要正好有这张桌子。 【参考方案1】:

我将创建一个复合主键 InkFormulaId,其中包含两个字段:idink_id,类似于

@Embeddable
public class InkFormulaId implements Serializable 

    private Integer id;

    private Integer inkId;

    // ...


@Entity
public class InkFormula 

    @EmbeddedId
    private InkFormulaId inkFormulaId;

    @ManyToOne
    @MapsId(value = "inkId")
    private Ink ink;

    private Integer percents;

    // ...

所以每个InkFormula 代表上表中的一行,具有唯一的主键(idinkId)及其percents

要获得您上面提到的映射,您必须将 SELECT all InkFormula 用于单个 id,然后在您的 Java 代码中按 inkId 对它们进行分组。

【讨论】:

我想要的是来自所有行的InkFormula 实体,具有相同的id,而不是每一行。这是一个挑战。 这是不可能的,因为单行有多个值会破坏“ACID”(en.wikipedia.org/wiki/ACID) 中的“A”,并且每个 JPA 实体代表单行。 好的,我们可以把InkFormulaid放到一个单独的ink_formula表中。然后让这个表被称为ink_formula_percent。但问题仍然存在:如何将ink_formulaink_formula_percent 加入到一个实体中。 为什么是另一个表 - 你试过我上面的例子吗?它导致一个表,您只需手动将InkFormula 对象分组,首先按id,然后按inkId 谢谢,Smutje,我试过了——效果很好。但是我懒得手动分组Map&lt;&gt;。我想让它由 Hibernate 和数据库完成。或者,如果没有太多工作,我可能不知道如何做到这一点?请参阅下面的替代解决方案。【参考方案2】:

另一种解决方案是使用@ElementCollection 注释并将数据分成两个表:

ink_formula    ink_formula_ink_percent
  ┌───┐    ┌──────────────┬──────┬───────┐
  │id │    │ink_formula_id│ink_id│percent│
  ├───┤    ├──────────────┼──────┼───────┤
  │ 1 │    │       1      │   1  │   20  │
  ├───┤    ├──────────────┼──────┼───────┤
  │ 2 │    │       1      │   2  │   55  │
  ├───┤    ├──────────────┼──────┼───────┤
  . . .    . . . . . . . . . . . . . . . . 

实体类将如下所示

@Entity
public class InkFormula 
    @Id
    @GeneratedValue
    int id;      

    @ElementCollection
    @CollectionTable(
            name="ink_formula_ink_percent",
            joinColumns=@JoinColumn(name="ink_formula_id")
    )
    @MapKeyJoinColumn(name="ink_id")
    @Column(name = "percent")
    private Map<Ink, Integer> inkPercents = new TreeMap<>();

    //...    

此设置满足独特的 InkFormula-Ink 要求,但我们需要为 InkFormula 提供单独的表。

下一个问题是如何确保每个InkFormula 的唯一性,因为具有不同ids 的两个InkFormulas 可以在它们的inkPercents 映射中保存相同的值。

【讨论】:

以上是关于来自具有非唯一 ID 的表的 JPA 实体的主要内容,如果未能解决你的问题,请参考以下文章

可以将@Id添加到映射到Spring boot Jpa中没有主键列的表的实体吗?

使用业务实体的自然ID作为主键

访问具有最新日期的 SQL 唯一记录,包括来自单个表的空日期

已解决:Spark 使非唯一字段按出现顺序具有 ID

更新具有来自同一表的最新相关 id 的表列

jpa 2.0 对具有 2 个 ID 的类的注释,指向两个不同的表