以多个外键作为复合主键的一对多实体映射

Entity mapping for one to many with multiple foreign keys as composite primary key

我浏览了 java 持久性文档和大量关于这种表示的 Whosebug 示例。有人可以验证我这样做是否正确吗?

类型 A 的实例与类型 C 的许多实例相关联。类型 B 的实例与类型 C 的许多实例相关联。C 的实例具有复合主键,该主键包含指向实体 a 和B.

表格:

一个

|------|
| A_ID |
|------|
| 1    |
| 2    |
| 3    |
| 4    |
| 5    |
|------|

其中A_ID是主键

B

|------|
| B_ID |
|------|
| 1    |
| 2    |
|------|

其中 B_ID 是主键

C

|------|------|
| A_ID | B_ID |
|------|------|
| 1    | 1    |
| 2    | 1    |
| 3    | 2    |
|------|------|

其中外键组合(A_ID、B_ID)为主键

实体:

@Entity
Class A {

    @Id
    @PrimaryKeyColumnName(name="A_ID")
    private Long id;


    @OneToMany(
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        mappedBy="A_ID"
    )
    Collection<C> values;


    public Long getId() {
        return this.id;
    }


    public Collection<C> getValues() {
        return this.values;
    }
}


@Entity
Class B {

    @Id
    @PrimaryKeyColumnName(name="B_ID")
    private Long id;

    public Long getId() {
        return this.id;
    }
}


@Entity
Class C {

    @EmbeddedId
    private CId cId;

    public CId getCId() {
        return this.cId;
    }
}

@Embeddable
Class CId {

    @ManyToOne
    @JoinColumn(name="A_ID")
    private A a;


    @ManyToOne
    @JoinColumn(name="B_ID")
    private B b;


    public Long getA() {
        return this.a;
    }

    public Long getBId() {
        return this.b;
    }

    @Override
    public int hashCode() {
        return Objects.hash(
            this.getA().getId(),
            this.getBId().getId()
        );
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof CId)) return false;
        CId that = (CId) obj;
        return Objects.equals(this.getA().getId(),
            that.getA().getId()) &&
            Objects.equals(this.getBId().getId(),
                that.getBId().getId());
    }
}

这就是 JPA 规范所说的 "derived identity"。你应该稍微不同地定义你的 类 。以下是代码的重要部分:

@Entity
Class A {

    @Id
    @Column(name="A_ID")
    private Long id;

    @OneToMany(
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        mappedBy="a"
    )
    Collection<C> values;

    ...
}


@Entity
Class B {

    @Id
    @Column(name="B_ID")
    private Long id;

    ...
}


@Entity
Class C {

    @EmbeddedId
    private CId cId;

    @ManyToOne
    @JoinColumn(name="A_ID")
    @MapsId("aId") // maps aId attribute of embedded id
    private A a;

    @ManyToOne
    @JoinColumn(name="B_ID")
    @MapsId("bId") // maps bId attribute of embedded id
    private B b;

    ...
}

@Embeddable
Class CId {

    private Long aId; // corresponds to PK type of A

    private Long bId; // corresponds to PK type of B

    ...
}

派生身份在 JPA 2.1 规范的第 2.4.1 节中进行了讨论。