微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

与复合主键的双向一对多关系

如何解决与复合主键的双向一对多关系

我正在尝试创建双向 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 中的复合主键列,您必须映射 userIdUserRoleEntityId 字段。

首先,您需要更改 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,因为这两个字段都将为空。
  • 另外,你不应该像上面的代码那样分配复合主键,你必须至少指定一个不同的字段(这里userIdname应该是唯一的组合)
  • 由于您与 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

的 ID

但以下内容似乎也很糟糕

    @OneToMany(cascade = CascadeType.ALL,mappedBy = "id.userId")
    List<UserRoleEntity> roles;

改成

    @OneToMany(cascade = CascadeType.ALL,mappedBy = "id")
    List<UserRoleEntity> roles;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。