复合主键与多个主键

Composite primary key vs multiple primary keys

拥有这个实体:

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如果我想使用两个或更多主要键。但如上所示,我可以忽略其中任何一个,只声明两个主键(这就是我的意思是“编译”)。所以问题是,为什么还要费心使用这些注释中的任何一个而只声明更多键?

生成 table:

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

hibernate documentation 中所述:

The restriction that a composite identifier has to be represented by a "primary key class" (e.g. @EmbeddedId or @IdClass) is only JPA-specific.

Hibernate does allow composite identifiers to be defined without a "primary key class" via multiple @Id attributes.

虽然映射比使用 @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));