【问题标题】:Composite primary key vs multiple primary keys复合主键与多个主键
【发布时间】:2021-09-10 11:41:00
【问题描述】:

拥有这个实体:

User.java

@Entity
@NoArgsConstructor
@Getter
@Setter
public class User {
    @Id
    private int id;
    private String username;
    @OneToMany(mappedBy = "owner")
    @MapKey(name = "friend_id")
    private Map<User, Friendship> friends = new HashMap<>();
}

友谊

@Entity
@Data
//@IdClass(Friendship.class)
public class Friendship implements Serializable {
    @Id
    private int owner_id;
    @Id
    private int friend_id;
    private String level;
    @ManyToOne
    @MapsId("owner_id")
    private User owner;
    @ManyToOne
    @MapsId("friend_id")
    private User friend;
}

如果我想使用两个或更多主键,我必须有@IdClass@EmbeddedId。但如上所示,我可以省略其中任何一个,只声明两个主键(这就是我的意思是“编译”)。所以问题是,为什么还要费心使用这些注释中的任何一个,而只是声明更多的键?

生成的表:

+-----------+--------------+------+-----+---------+-------+
| Field     | Type         | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| owner_id  | int          | NO   | PRI | NULL    |       |
| friend_id | int          | NO   | PRI | NULL    |       |
| level     | varchar(255) | YES  |     | NULL    |       |
+-----------+--------------+------+-----+---------+-------+

【问题讨论】:

  • “编译”和“拥有多个主键”是什么意思?您的研究表明为什么存在 PK 以及 PK 或备用密钥是什么以及“使用 @IdClass 明确声明它的好处”? How to Ask
  • @philipxy 如果我想使用 两个 或更多主键,我必须有 @IdClass@EmbeddedId。但如上所示,我可以省略任何一个,只声明两个主键(这就是我的意思是“编译”)。所以问题是,为什么还要费心使用这些注释中的任何一个,而只是声明更多的键?
  • 请通过编辑而非 cmets 进行澄清。 PS只能有一个PK。可以有多个“候选”/唯一键——其中一个可以是 PK。但是构成复合 PK 的 2 列中的任何一个都不是 PK 的候选者。两者都不唯一地确定一个实体。阅读有关键的信息。请在考虑发布问题之前进行研究。
  • 这个实体生成的表的定义, 2个主键(owner_idfriend_id)。所以我没看懂你的说法,*只能有一个PK。
  • 该表没有 2 个 PK。它有 2 个 FK,列的值是其他地方的 PK。

标签: spring hibernate primary-key composite-primary-key


【解决方案1】:

正如hibernate documentation中提到的:

复合标识符必须由“主键类”(例如 @EmbeddedId@IdClass)表示的限制仅适用于 JPA。

Hibernate 确实允许通过多个 @Id 属性在没有“主键类”的情况下定义复合标识符。

虽然映射比使用@EmbeddedId@IdClass 简单得多,但实体实例和实际标识符之间没有分离。要查询此实体,必须将实体本身的实例提供给持久化上下文。

@Entity
public class Friendship implements Serializable {

    /*
        It's better to use object wrapper classes instead of the corresponding 
        primitive types. Because, for example, uninitialized Integer is null,
        but uninitialized int is 0 that can be a legal id.
    */
    @Id
    private Integer ownerId;

    @Id
    private Integer friendId;

    public Friendship() {

    }

    public Friendship(Integer ownerId, Integer friendId) {
        this.ownerId = ownerId;
        this.friendId = friendId;
    }
    // ...
}

Friendship friendship = entityManager.find(Friendship.class, new Friendship(ownerId, friendId));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 2014-04-19
    • 1970-01-01
    • 2020-01-08
    • 2018-10-25
    • 1970-01-01
    相关资源
    最近更新 更多