![](/img/trans.png)
[英]Bidirectional OneToMany relationship with composite entity key cause null identifier
[英]Bidirectional OneToMany relationship with composite primary key
我正在尝试创建双向 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)
);
我想用 hibernate 来表示它。
我的 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 object:
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'
如何修复它并以简单的方式实现此映射?
要 map UserEntity
的主键和UserRoleEntityId
UserRoleEntity
userId
字段。
首先,您需要更改UserEntity
中的映射,现在您有
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "id.userId")
List<UserRoleEntity> roles;
你需要把它改成这个
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user")
List<UserRoleEntity> roles;
原因是用户 class 在这里是父级,我们需要在子实体中指定将驱动两者之间关系的字段。
现在,您的子表UserRoleEntity
中的映射很好,它将 map 复合主键中的userId
字段映射到UserEntity
表中的字段userId
,因此本质上您的用户实体主键将被共享。
现在,您必须注意这里的几件事
UserRoleEntity
实体都将被视为 equals,因为这两个字段都是 null。userId
和name
应该是唯一的组合)UserEntity
表共享主键,因此您需要注意,在创建新用户时,只有在 hibernate 执行插入语句后才可用(除非它是手动生成的键,这里不是这种情况),因此UserRoleEntityId
中的name
属性在添加新角色时必须是唯一的。最后但是同样重要的
您应该在一个操作中以双向关系同步您的两个实体。
1因此,您正在使用 setter 将role
设置为UserEntity
,但您应该add
function 或以给定方式编写的 setter
public void add(UserEntityRole role) {
roles.add(role);
role.setUser(this);
}
否则,如果客户端忘记在两个实体上调用 setter 并且表约束不存在,那么它可以将外键作为 null 保存在表中。
脚注
@ManyToOne
@MapsId("id")
protected UserEntity user;
使用@MapsId
您可以引用其他实体的 id 字段。 因此,如果您将@MapsId
放在 UserRoleEntity 上,那么您引用的 id 就是来自 UserEntity 的
但以下似乎也不好
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "id.userId")
List<UserRoleEntity> roles;
将其更改为
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "id")
List<UserRoleEntity> roles;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.