繁体   English   中英

与复合主键的双向 OneToMany 关系

[英]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 ,因此本质上您的用户实体主键将被共享。

现在,您必须注意这里的几件事

  • 您需要在复合主键中实现 equals 和 hashcode,否则您的代码生成的所有新UserRoleEntity实体都将被视为 equals,因为这两个字段都是 null。
  • 此外,您不应像在上面的代码中那样分配复合主键,您必须指定至少一个不同的字段(这里userIdname应该是唯一的组合)
  • 由于您与UserEntity表共享主键,因此您需要注意,在创建新用户时,只有在 hibernate 执行插入语句后才可用(除非它是手动生成的键,这里不是这种情况),因此UserRoleEntityId中的name属性在添加新角色时必须是唯一的。

最后但是同样重要的

您应该在一个操作中以双向关系同步您的两个实体。

1因此,您正在使用 setter 将role设置为UserEntity ,但您应该add function 或以给定方式编写的 setter

public void add(UserEntityRole role) {
    roles.add(role);
    role.setUser(this);
}

否则,如果客户端忘记在两个实体上调用 setter 并且表约束不存在,那么它可以将外键作为 null 保存在表中。

脚注


1有关此检查链接文章的更多信息。

map hibernate双向关系的最佳方式

    @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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM