java为什么Hibernate希望复合主键有一个单独的表?(@EmbeddedId)
我正在做一个排行榜系统作为一个爱好项目。我有三个实体:
Player
在排行榜上代表一名球员StatKey
代表排行榜上某个统计数据的键PlayerStat
代表给定玩家的特定属性,应该有一个由{}和{ }组成的复合主键,以及一个将其映射到相关{ }和父 Player
实体的外键李>
我对JPA的整体印象非常深刻。在创建复合主键时,我仍然有点不确定@EmbeddedId
与@IdClass
是否是我的最佳选择。从我在网上读到的内容来看,似乎最好的方法是使用@EmbeddedId
然而,这会导致一些非常奇怪的查询和表结构。我希望有三张桌子:
- 玩家(
pk(id)
,username
,total_level
,total_experience
,date_created
,last_updated
) - 玩家统计(
pk(player_id, stat_id)
,level
,experience
,date_created
,last_updated
) - statkey(
pk(id)
,key
)
以及两个外键:
player_stat
(player_id
)引用player
player_stat
(stat_id
)引用statkey
所有这些都按预期生成。但是,除了这三个表之外,还生成了第四个表:
player_stats
(pk(player_id, stats_player_id, stats_stat_id)
)
以及以下外键:
player_stats
(stats_player_id
,stats_stat_id
)引用player_stat
player_stats
(player_id
)引用player
奇怪的第四个表不仅是DDL问题,而且在尝试查询数据库时也会用到它。我不知所措。任何帮助都将不胜感激
代码:
@Entity
@Table(name = "player", uniqueConstraints = @UniqueConstraint(name = "player_uk_username", columnNames = "username"))
public class Player {
@Id
@Column(nullable = false)
private UUID id;
@Column(nullable = false)
private String username;
@Column(name = "total_level", nullable = false)
private int totalLevel;
@Column(name = "total_experience", nullable = false)
private long totalExperience;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<PlayerStat> stats;
@Column(name = "date_created", nullable = false, updatable = false)
private OffsetDateTime dateCreated;
@Column(name = "last_updated", nullable = false)
private OffsetDateTime lastUpdated;
protected Player() {
}
public Player(final UUID id, final String username, final Set<PlayerStat> stats) {
this.id = id;
this.username = username;
this.stats = stats;
}
@PrePersist
public void prePersist() {
this.dateCreated = OffsetDateTime.now();
this.lastUpdated = this.dateCreated;
}
@PreUpdate
public void preUpdate() {
this.lastUpdated = OffsetDateTime.now();
}
}
@Entity
@Table(name = "statkey", uniqueConstraints = @UniqueConstraint(name = "statkey_uk_key", columnNames = "key"))
public class StatKey {
@Id
@Column(nullable = false)
private int id;
@Column(nullable = false)
private String key;
@Column(name = "date_created", nullable = false, updatable = false)
private OffsetDateTime dateCreated;
@Column(name = "last_updated", nullable = false)
private OffsetDateTime lastUpdated;
protected StatKey() {
}
public StatKey(final int id, final String key) {
this.id = id;
this.key = key;
}
@PrePersist
public void prePersist() {
this.dateCreated = OffsetDateTime.now();
this.lastUpdated = this.dateCreated;
}
@PreUpdate
public void preUpdate() {
this.lastUpdated = OffsetDateTime.now();
}
}
@Embeddable
public class PlayerStatKey implements Serializable {
@JsonUnwrapped(prefix = "player")
@ManyToOne
@JoinColumn(name = "player_id", referencedColumnName = "id", foreignKey = @ForeignKey(name = "player_stat_fk_player_id"))
private Player player;
@JsonUnwrapped(prefix = "stat")
@ManyToOne
@JoinColumn(name = "stat_id", referencedColumnName = "id", foreignKey = @ForeignKey(name = "player_stat_fk_statkey_id"))
private StatKey statKey;
protected PlayerStatKey() {
}
public PlayerStatKey(final Player player, final StatKey statKey) {
this.player = player;
this.statKey = statKey;
}
}
@Entity
@Table(name = "player_stat")
public class PlayerStat {
@EmbeddedId
@JsonUnwrapped
private PlayerStatKey key;
@Column(nullable = false)
private int level;
@Column(nullable = false)
private int experience;
@Column(name = "date_created", nullable = false, updatable = false)
private OffsetDateTime dateCreated;
@Column(name = "last_updated", nullable = false)
private OffsetDateTime lastUpdated;
protected PlayerStat() {
}
public PlayerStat(final PlayerStatKey key, final int level, final int experience) {
this.key = key;
this.level = level;
this.experience = experience;
}
@PrePersist
public void prePersist() {
this.dateCreated = OffsetDateTime.now();
this.lastUpdated = this.dateCreated;
}
@PreUpdate
public void preUpdate() {
this.lastUpdated = OffsetDateTime.now();
}
}
预期:
create table player (id binary not null, date_created timestamp not null, last_updated timestamp not null, total_experience bigint not null, total_level integer not null, username varchar(255) not null, primary key (id))
create table player_stat (date_created timestamp not null, experience integer not null, last_updated timestamp not null, level integer not null, player_id binary not null, stat_id integer not null, primary key (player_id, stat_id))
create table statkey (id integer not null, date_created timestamp not null, key varchar(255) not null, last_updated timestamp not null, primary key (id))
alter table player add constraint player_uk_username unique (username)
alter table statkey add constraint statkey_uk_key unique (key)
alter table player_stat add constraint player_stat_fk_player_id foreign key (player_id) references player
alter table player_stat add constraint player_stat_fk_statkey_id foreign key (stat_id) references statkey
现实:
create table player (id binary not null, date_created timestamp not null, last_updated timestamp not null, total_experience bigint not null, total_level integer not null, username varchar(255) not null, primary key (id))
create table player_stat (date_created timestamp not null, experience integer not null, last_updated timestamp not null, level integer not null, player_id binary not null, stat_id integer not null, primary key (player_id, stat_id))
create table player_stats (player_id binary not null, stats_player_id binary not null, stats_stat_id integer not null, primary key (player_id, stats_player_id, stats_stat_id))
create table statkey (id integer not null, date_created timestamp not null, key varchar(255) not null, last_updated timestamp not null, primary key (id))
alter table player add constraint player_uk_username unique (username)
alter table player_stats add constraint UK_9xeo0h5xwn53uq68knp5llr04 unique (stats_player_id, stats_stat_id)
alter table statkey add constraint statkey_uk_key unique (key)
alter table player_stat add constraint player_stat_fk_player_id foreign key (player_id) references player
alter table player_stat add constraint player_stat_fk_statkey_id foreign key (stat_id) references statkey
alter table player_stats add constraint FKrc4tc1jspyawwida0o2dtcp4j foreign key (stats_player_id, stats_stat_id) references player_stat
alter table player_stats add constraint FKfekdv3tvbrd0b8u6c2fuxk5fw foreign key (player_id) references player
# 1 楼答案
问题似乎是缺少从父实体到子实体的映射
解决方案是在
StatKey
中添加以下内容:以及在
Player
中的@OneToMany
注释中添加mappedBy = "primaryKey.player"
# 2 楼答案
除了oa54所说的:
这与Spring数据JPA无关。如果SQL是自动生成的,我假设您(无意中)使用了JPA提供程序的SQL生成功能,可能是Hibernate。我将相应地更新问题标题
我强烈建议您使用专用的SQL迁移工具(Flyway或Liquibase)来编写定义数据结构的SQL,因为在某个时候您必须使用类似的工具。一旦你的类需要新的字段,你就需要迁移数据,改变表,而不是重新创建它们等等