Quarkus Hibernate配置@EmbeddedId仍报实体无标识符异常
问题根因
抛出No identifier specified for entity异常的核心原因是JPA访问类型配置与注解位置不匹配,同时代码中还存在多处映射逻辑错误,具体如下:
- 你在
UserRole类上标注了@Access(AccessType.PROPERTY),该模式下JPA只会从实体类的getter方法上读取映射注解,不会扫描私有字段上的注解。但你将@EmbeddedId标注在了私有字段上,Hibernate无法识别到主键配置,直接触发启动异常。 - 复合主键字段命名不符合JavaBean规范:主键字段名写为大驼峰开头的
UserRolePK,会导致属性解析失败。 - 复合主键类
UserRolePK配置错误:User和Role实体的主键均为UUID类型,但主键类中两个字段写为Long类型;且配置的列名user_id/role_id与实际表的外键列id_users/id_roles不匹配。 - 关联关系重复映射、配置错误:你既在复合主键中映射了用户、角色关联列,又单独定义了
@OneToOne的roleId/userId字段映射相同列,会触发列重复映射冲突;updatedBy字段的外键配置错误,列名写为update_by(与描述的updated_by不符),且错误关联到User表的name字段而非主键id。
修复方案
1. 修正复合主键类UserRolePK
调整字段类型为UUID,列名与实际表结构对齐:
package offgrid.models; import javax.persistence.Column; import javax.persistence.Embeddable; import java.io.Serializable; import java.util.Objects; import java.util.UUID; @Embeddable public class UserRolePK implements Serializable { @Column(name = "id_users") private UUID userId; @Column(name = "id_roles") private UUID roleId; public UUID getUserId() { return userId; } public void setUserId(UUID userId) { this.userId = userId; } public UUID getRoleId() { return roleId; } public void setRoleId(UUID roleId) { this.roleId = roleId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserRolePK that = (UserRolePK) o; return Objects.equals(userId, that.userId) && Objects.equals(roleId, that.roleId); } @Override public int hashCode() { return Objects.hash(userId, roleId); } }
2. 修正UserRole实体类
删除不必要的@Access(AccessType.PROPERTY)注解(默认使用字段访问模式,无需额外配置),修正字段命名,通过@MapsId绑定关联关系与复合主键字段,避免重复映射,修正updatedBy字段配置:
package offgrid.models; import io.quarkus.hibernate.reactive.panache.PanacheEntityBase; import javax.persistence.*; import java.time.LocalDateTime; @Entity @Table(name = "user_roles") public class UserRole extends PanacheEntityBase { @EmbeddedId private UserRolePK userRolePK; @MapsId("roleId") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "id_roles", referencedColumnName = "id") private Role role; @MapsId("userId") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "id_users", referencedColumnName = "id") private User user; @Column(name = "created_at") private LocalDateTime createdAt; @Column(name = "updated_at") private LocalDateTime updatedAt; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "updated_by", referencedColumnName = "id") private User updatedBy; public UserRolePK getUserRolePK() { return userRolePK; } public void setUserRolePK(UserRolePK userRolePK) { this.userRolePK = userRolePK; } public Role getRole() { return role; } public void setRole(Role role) { this.role = role; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public LocalDateTime getCreatedAt() { return createdAt; } public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } public LocalDateTime getUpdatedAt() { return updatedAt; } public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } public User getUpdatedBy() { return updatedBy; } public void setUpdatedBy(User updatedBy) { this.updatedBy = updatedBy; } }
注:用户-角色关系是典型的多对多关联,中间表实体上用户、角色的关联都应该是
@ManyToOne而非@OneToOne,否则会限制一个用户只能绑定一个角色、一个角色只能绑定一个用户,不符合常规业务逻辑。
额外优化建议
User实体中自关联的更新人字段命名不规范,建议调整为与列名一致的updatedBy,避免后续映射解析出错:
// 替换User实体类中原有的user字段定义 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "updated_by") private User updatedBy; // 对应修改getter/setter public User getUpdatedBy() { return updatedBy; } public void setUpdatedBy(User updatedBy) { this.updatedBy = updatedBy; }
内容的提问来源于stack exchange,提问作者Lucas Terranova
相关产品推荐
相关产品推荐

