如何解决与复合主键的双向一对多关系
我正在尝试创建双向 OnetoMany 关系,
其中一个外键是复合主索引的一部分。
我目前有这个架构:
CREATE TABLE users
(
id INTEGER UNIQUE AUTO_INCREMENT,PRIMARY KEY (id)
);
CREATE TABLE user_roles
(
name VARCHAR(16) NOT NULL,userId INTEGER,PRIMARY KEY (userId,name),FOREIGN KEY (userId) REFERENCES users (id)
);
我想在休眠中表示它。
我的 UserEntity 被映射为:
@Entity
@Table(name = "users")
public class UserEntity implements User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Integer id;
@OnetoMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy = "id.userId")
List<UserRoleEntity> roles;
@Override
public Integer getId() {
return id;
}
@Override
public List<String> getRoles() {
return roles.stream()
.map(UserRoleEntity::getName)
.collect(Collectors.toList());
}
public void setId(Integer id) {
this.id = id;
}
public void setRoles(List<UserRoleEntity> roles) {
this.roles = roles;
}
}
还有我的 UserRoleEntity:
@Embeddable // I haven't yet implemented hashcode etc
class UserAndRoleNameUniqueId implements Serializable {
protected Integer userId;
protected String name;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity
@Table(name = "user_roles")
public class UserRoleEntity {
@EmbeddedId
protected UserAndRoleNameUniqueId id = new UserAndRoleNameUniqueId();
@ManyToOne
@MapsId("userId")
protected UserEntity user;
public void setUserId(Integer userId) {
this.id.userId = userId;
}
public Integer getUserId() {
return user.getId();
}
public void setUser(UserEntity user) {
this.user = user;
}
public String getName() {
return id.name;
}
public void setName(String name) {
this.id.name = name;
}
}
我通过以下方式保存 UserEntity 对象:
var userEntity = new UserEntity();
userEntity.setRoles(
user.getRoles().stream().map(role -> {
var userRoleEntity = new UserRoleEntity();
userRoleEntity.setName(role);
return userRoleEntity;
}).collect(Collectors.toList())
);
sessionFactory.getCurrentSession().save(userEntity);
return userEntity;
然后我得到 Caused by: java.sql.sqlException: UnkNown column 'userroleen_.user_id' in 'field list'
如何修复它并以简单的方式实现此映射?
解决方法
要将 UserEntity
的主键映射到 UserRoleEntity
中的复合主键列,您必须映射 userId
的 UserRoleEntityId
字段。
首先,您需要更改 UserEntity
中的映射,现在您已经
@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy = "id.userId")
List<UserRoleEntity> roles;
你需要改成这个
@OneToMany(cascade = CascadeType.ALL,mappedBy = "user")
List<UserRoleEntity> roles;
原因是这里的用户类是父类,我们需要在子实体中指定将驱动两者之间关系的字段。
现在,在您的子表 UserRoleEntity
中进行映射就可以了,它将复合主键中的 userId
字段映射到 userId
表中的字段 UserEntity
,因此本质上,您的用户实体主键将被共享。
现在,你必须注意这里的一些事情
- 您需要在复合主键中实现 equals 和 hashcode,否则您的代码生成的所有新
UserRoleEntity
实体都将被视为 equals,因为这两个字段都将为空。 - 另外,你不应该像上面的代码那样分配复合主键,你必须至少指定一个不同的字段(这里
userId
和name
应该是唯一的组合) - 由于您与
UserEntity
表共享主键,因此您需要注意,在创建新用户时,只有在休眠执行插入语句后才可用(除非它是手动生成的键,此处并非如此),因此name
中的UserRoleEntityId
属性在添加新角色时必须是唯一的。
最后但并非最不重要的
您应该在一项操作中以双向关系同步您的两个实体。
1所以您正在使用 setter 将 role
设置为 UserEntity
,但您应该有一个 add
函数或以给定方式编写的 setter
public void add(UserEntityRole role) {
roles.add(role);
role.setUser(this);
}
否则,如果客户端忘记在实体上调用 setter 并且表约束不存在,那么它可以将表中的外键保存为空。
脚注
1有关此检查链接文章的更多信息。
Best way to map hibernate bidirectional relationship
, @ManyToOne
@MapsId("id")
protected UserEntity user;
使用 @MapsId
引用另一个实体的 id 字段。因此,如果您将 @MapsId
放在 UserRoleEntity 上,那么您引用的 ID 是来自 UserEntity
但以下内容似乎也很糟糕
@OneToMany(cascade = CascadeType.ALL,mappedBy = "id.userId")
List<UserRoleEntity> roles;
改成
@OneToMany(cascade = CascadeType.ALL,mappedBy = "id")
List<UserRoleEntity> roles;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。