【问题标题】:Save entity with FK "reuse"使用 FK“重用”保存实体
【发布时间】:2020-02-28 04:49:23
【问题描述】:

我正在使用 Spring + Hibernate JPAA。

我有一个实体:

A
  id (PK)
  Set<B> b;
  otherProps…

B
  id (PK)
  String name; (unique)
  otherProps…

并且有一个 M 到 M 表链接 A.id、B.id。

如果用户创建实体:

A.id = 0
A.b.id = 0
A.b.Name = "Admin";

它会保存,但会在 B 表中创建一个新条目。将 B 视为系统定义的“角色”表,它不应该改变。所以我希望它重新使用 Admin 并自动填充 id。该对象来自 REST api……在这种情况下,调用者是否应该知道 ID 和名称?或者他们应该能够通过 id 或 Name 填充?

如何处理这种情况?还是去掉id,取名字为PK会更好?

编辑:澄清...

用户实体 角色实体 UserRole 表与 User.Id、Role.Id M2M 关系。

角色表包含​​: 1 位用户 2 管理员 3 超级用户

那是我定义的固定表。不允许调用者添加角色。

所以如果User1是User,那么M2M表1,1中就会有一个条目。

现在,想象一下如果有人传入一个角色为“用户”的新用户对象 User2。就像现在一样,它在 Roles id=4 value=User (重复条目)中创建一个条目,并在 M2M 表中创建一个 1,4,其中预期行为是 1,1,即重用现有的“用户”角色。

基本上,我想调用者知道用户 id 是有意义的,但我不确定调用者知道角色 id 是否有意义?似乎他们会知道可能的角色。有点像枚举类型的行为?

@实体 @Table(name="客户") 公共类客户{

@ApiModelProperty(notes="Id of the customer.", required=true, value="100000")
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long customerId;
@NotNull
@Size(min=2, max=64)
@ApiModelProperty(notes="First name of the customer.", required=true, value="John")
private String firstName;
@NotNull
@Size(min=2, max=64)
@ApiModelProperty(notes="Last name of the customer.", required=true, value="Smith")
private String lastName;
@ManyToMany(cascade={ CascadeType.MERGE })
@JoinTable(name="CustomerRoles",
           joinColumns={ @JoinColumn(name="CustomerId") },
           inverseJoinColumns={ @JoinColumn(name="RoleId") }
)
private Set<Role> roles = new HashSet<>();

public Long getCustomerId() {
    return this.customerId;
}

public String getFirstName() {
    return this.firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return this.lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public List<Role> getRoles() {
    return new ArrayList<Role>(this.roles);
}

@实体 @Table(name="角色") 公共类角色 {

@ApiModelProperty(notes="Id of the role.", required=true, value="1")
@Id
//@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long roleId;
@NotNull
@Size(min=2, max=64)
@ApiModelProperty(notes="Name of the role.", required=true, value="Admin")
private String name;

@ManyToMany(mappedBy = "roles")
private Set<Customer> roles = new HashSet<Customer>();

public Long getRoleId() {
    return this.roleId;
}

public String getName() {
    return this.name;
}

public void setName(String name) {
    this.name = name;
}

}

【问题讨论】:

    标签: java spring hibernate


    【解决方案1】:

    如果没有完整的代码,很难弄清楚发生了什么。 但总的来说,您应该考虑以下概念:

    带有JoinTable注解的实体是关系的Owner。这意味着如果您在“所有者”上启用Cascade.Merge,向其中添加新记录,将向中间表添加另一条记录。

    一个例子

    Role实体:

    {
      @ManyToMany(cascade = {CascadeType.MERGE})
        @JoinTable(name = "tb_role_privilege",
                joinColumns = @JoinColumn(name = "role_id"),
                inverseJoinColumns = @JoinColumn(name = "privilege_id"))
        private Set<Privilege> privileges = new HashSet<>();
    }
    

    Privilege实体:

    {
          @ManyToMany(mappedBy = "privileges")
        private Set<Role> roles = new HashSet<Role>();
    }
    

    这里,Role 是关系的所有者。当您使用id=0 创建角色对象并使用ids=[0 1 2] 填充其权限列表并调用save 时,它会将角色记录插入tb_role,并且还会插入(0,0),( 0,1) 和 (0,2) 到中间表 tb_role_privilege但它不会向 tb_privilege 插入任何内容。所以你需要在插入角色记录之前将权限记录插入tb_privilege

    对于m2m 关系来说,这是一种很好且简单的方法。如果它不满足您的方案,请为您的代码提供更多信息。

    【讨论】:

    • 您好,我已经更新了一些问题,这是否清楚?如果没有,我会发布实体。
    • @SledgeHammer 你的场景是我解释的确切场景。将用户实体设为所有者并在其上添加 cascade.merge。基于示例,角色实体是您的用户,权限实体是您的角色
    • 嗨,我已更新帖子以显示我的 2 个实体。如果我完全匹配 ENTIRE 角色实体 1,Admin 那么它可以工作。如果我输入 0,Admin,则会引发 FK 约束异常。我认为不同之处在于,我的角色的 ID 从 1 开始,而我只想传入 NAME(并匹配客户的行为,其中 0 将自动填写响应中的正确 ID)。或者在这种情况下,它是否也需要传入 id?这对客户来说不是问题,因为这是一个生成的 ID。
    • 我不确定是否需要按名称查询角色表并手动修补请求中的 id,或者是否有一些休眠技巧。
    • @SledgeHammer 按名称获取角色记录并传递其 id 是一种标准且好的方法。无需技巧或变通方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多