复合主键与多个主键
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));
拥有这个实体:
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));