JPA Hibernate @OneToOne关联报错:无法确定UserDetail类型
解决JPA/Hibernate双向@OneToOne关联的类型识别错误
我来帮你搞定这个问题,你遇到的Could not determine type错误,核心是两个配置失误导致的,咱们一步步修正:
错误原因分析
- 注解位置混合使用:你的
User类里,@OneToOne注解直接放在字段userDetail上,但其他JPA注解(比如@Id、@Basic)都放在getter方法上。Hibernate默认只会识别一种位置的注解(要么全字段,要么全getter),混合使用会让它无法正确识别关联属性的类型。 - 双向关联配置冲突:
UserDetail类的@OneToOne同时用了mappedBy和@JoinColumn——这俩属性是互斥的:mappedBy表示当前类是关联的被动方(由对方维护外键),而@JoinColumn是标记当前类为主控方(拥有外键),不能同时用。
具体修正步骤
步骤1:统一所有JPA注解的位置
咱们选择把所有注解都放在字段上(更简洁直观),修改后的User类如下:
@Entity @Table(name = "users") public class User { @Id @Column(name = "username", nullable = false, length = 50) private String username; @Basic @Column(name = "password", nullable = false, length = 50) private String password; @Basic @Column(name = "enabled", nullable = false) private int enabled; // 被动方:由UserDetail维护关联关系 @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false) private UserDetail userDetail; public User() { } // 以下是普通的getter/setter,无需再加注解 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getEnabled() { return enabled; } public void setEnabled(int enabled) { this.enabled = enabled; } public UserDetail getUserDetail() { return userDetail; } public void setUserDetail(UserDetail userDetail) { this.userDetail = userDetail; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return enabled == user.enabled && Objects.equals(username, user.username) && Objects.equals(password, user.password); } @Override public int hashCode() { return Objects.hash(username, password, enabled); } }
步骤2:修正UserDetail的关联配置
从你的表结构来看,UserDetail的主键username同时作为关联User的外键(属于共享主键的@OneToOne关联),所以我们移除冲突的mappedBy,改用@PrimaryKeyJoinColumn来标记共享主键关系:
@Entity @Table(name = "user_detail") public class UserDetail { @Id @Column(name = "username", nullable = false, length = 50) private String username; @Basic @Column(name = "first_name", nullable = false, length = 50) private String firstName; @Basic @Column(name = "last_name", nullable = false, length = 50) private String lastName; @Basic @Column(name = "email", nullable = false, length = 50) private String email; @Basic @Column(name = "country", nullable = false, length = 50) private String country; // 主控方:用@PrimaryKeyJoinColumn指定共享主键 @OneToOne(fetch = FetchType.LAZY) @PrimaryKeyJoinColumn(name = "username", referencedColumnName = "username") private User user; public UserDetail() { } // 普通getter/setter,无需注解 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserDetail that = (UserDetail) o; return Objects.equals(username, that.username) && Objects.equals(firstName, that.firstName) && Objects.equals(lastName, that.lastName) && Objects.equals(email, that.email) && Objects.equals(country, that.country); } @Override public int hashCode() { return Objects.hash(username, firstName, lastName, email, country); } }
额外说明
如果你不想用共享主键,而是想在users表中新增user_detail_id外键字段,只需要调整主控方:把User作为主控方,在userDetail字段上添加@JoinColumn(name = "user_detail_id"),同时UserDetail的user字段使用mappedBy = "userDetail"即可。
内容的提问来源于stack exchange,提问作者Raspberry
相关产品推荐
相关产品推荐

