【问题标题】:Auto populate data of primary keys of different tables into another one table as foreign keys using JPA/Hibernate使用 JPA/Hibernate 将不同表的主键数据作为外键自动填充到另一个表中
【发布时间】:2020-07-13 06:04:27
【问题描述】:

下面是预期的表结构:

Users -> user_id (PK)
RoleA -> role_a_id (PK)
RoleB -> role_b_id (PK)
User_Roles -> user_id (FK), role_a_id (FK), role_a_id (FK)

以下是实体类:

@Table(name = "users")
@Entity
public class Users {

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "user_id")
    private int userId;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinTable(
      name = "user_roles", 
      joinColumns = @JoinColumn(name = "user_id"), 
      inverseJoinColumns = @JoinColumn(name = "role_a_id"))
    Set<RoleA> rolesA;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(
      name = "user_roles", 
      joinColumns = @JoinColumn(name = "user_id"), 
      inverseJoinColumns = @JoinColumn(name = "role_b_id"))
    RoleB rolesB;

    public Set<RoleA> getRolesA() {
        return rolesA;
    }

    public void setRolesA(Set<RoleA> rolesA) {
        this.rolesA = rolesA;
    }

    public RoleB getRolesB() {
        return rolesB;
    }

    public void setRolesB(RoleB rolesB) {
        this.rolesB = rolesB;
    }
}
@Table(name = "role_a")
@Entity
public class RoleA {

    public int getRoleAId() {
        return roleAId;
    }

    public void setRoleAId(int roleAId) {
        this.roleAId = roleAId;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "role_a_id")
    private int roleAId;
}
@Table(name = "role_b")
@Entity
public class RoleB {

    public int getRoleBId() {
        return roleBId;
    }

    public void setRoleBId(int roleBId) {
        this.roleBId = roleBId;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "role_b_id")
    private int roleBId;
}

插入数据的代码:

RoleA roleA = new RoleA();
roleA.setRoleAId(1);
Set<RoleA> rolesA = new HashSet<>();
rolesA.add(roleA);

RoleB roleB = new RoleB();
roleB.setRoleBId(2);

Users user = new Users();
user.setUserId(10);
user.setRolesA(rolesA);
user.setRolesB(roleB);

userDAO.addUser(user);

在启动服务器时,所有表都按预期创建,但是一旦按上述方式尝试插入数据,就会出现以下错误:

2020-04-01 17:14:35.203 DEBUG 30244 --- [p-nio-80-exec-2] org.hibernate.SQL                        : insert into users (user_id) values (?)
2020-04-01 17:14:35.206 TRACE 30244 --- [p-nio-80-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [INTEGER] - [95]
2020-04-01 17:14:35.249 DEBUG 30244 --- [p-nio-80-exec-2] org.hibernate.SQL                        : insert into user_roles (role_b_id, user_id) values (?, ?)
2020-04-01 17:14:35.250 TRACE 30244 --- [p-nio-80-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [INTEGER] - [2]
2020-04-01 17:14:35.251 TRACE 30244 --- [p-nio-80-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [INTEGER] - [95]
2020-04-01 17:14:35.310  WARN 30244 --- [p-nio-80-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: 23502
2020-04-01 17:14:35.311 ERROR 30244 --- [p-nio-80-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : ERROR: null value in column "role_a_id" violates not-null constraint
  Detail: Failing row contains (2, 95, null).
2020-04-01 17:14:35.393 DEBUG 30244 --- [p-nio-80-exec-2] c.h.refoearn.controller.UserController   : DataIntegrityViolationException while adding user: could not execute statement; SQL [n/a]; constraint [role_a_id]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement

根据 User_Roles 的日志插入语句,不考虑插入 role_a_id 并声明为非 null,抛出异常。

Can someone please suggest the root cause why role_a_id is not being taken care while insertion in User_Roles ?
What is the missing piece stopping it to happen ?
Any other suggestion to fulfill the requirement if shared approach is not correct ?

【问题讨论】:

    标签: hibernate jpa join jpa-2.0 multiple-join-rows


    【解决方案1】:

    看起来在意图和实施上可能存在一些差距。 User_Roles 表的确切用途尚不清楚。这张桌子是什么关系?是否试图将Users 表与

    1. RoleA
    2. RoleB
    3. RoleARoleB

    如果是第一种情况,则role_b_id 不应该存在。如果是第二种情况,则不应存在 role_a_id

    对于第三种情况,您可能需要重新考虑您的设计。想一想您要定义什么关系,即以下任何一种都可能适用

    1. 用户与RoleARoleB 相关,但RoleARoleB 不相互依赖。
    2. 用户与RoleARoleBRoleARoleB都相关,相互依赖。

    查看Users 类,因为它与RoleBOneToOne 关系,我的第一个猜测是RoleARoleB 不相互依赖的第一个点。考虑到您应该完全删除User_Roles 表中的role_b_id 列,并将其直接添加为Users 表的一部分,或者为其创建一个不同的表。

    【讨论】:

      【解决方案2】:

      经过分析和不同的建议,我发现如果我们将 User_Roles 表中的 2 列作为主键,我会发现它会有冗余数据。从 DB Normalization 的角度来看,这将是一个糟糕的设计。如果所有三列都是唯一的并且充当主键,那么我看不到该表的任何特定用途。

      因此,更好的解决方案是让 roleId 与 user 表本身具有一对一的映射关系,并与 User_Roles 表保持一对多的映射关系。所以,最终的表结构如下:

      Users -> user_id (PK), role_b_id (FK, One To One)
      RoleA -> role_a_id (PK)
      RoleB -> role_b_id (PK)
      User_Roles -> user_id (FK), role_a_id (FK, One To Many)
      

      这个结构可以通过如下修改users表来实现:

      @Table(name = "users")
      @Entity
      public class Users {
      
          public int getUserId() {
              return userId;
          }
      
          public void setUserId(int userId) {
              this.userId = userId;
          }
      
          @Id
          @GeneratedValue(strategy = GenerationType.AUTO)
          @Column(name = "user_id")
          private int userId;
      
          @OneToMany(cascade = CascadeType.ALL)
          @JoinTable(
            name = "user_roles", 
            joinColumns = @JoinColumn(name = "user_id"), 
            inverseJoinColumns = @JoinColumn(name = "role_a_id"))
          Set<RoleA> rolesA;
      
          @OneToOne(cascade = CascadeType.ALL)
          @JoinColumn(name = "rolesB")
          RoleB rolesB;
      
          public Set<RoleA> getRolesA() {
              return rolesA;
          }
      
          public void setRolesA(Set<RoleA> rolesA) {
              this.rolesA = rolesA;
          }
      
          public RoleB getRolesB() {
              return rolesB;
          }
      
          public void setRolesB(RoleB rolesB) {
              this.rolesB = rolesB;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-22
        • 2013-12-09
        • 2022-01-22
        • 1970-01-01
        • 2020-05-15
        相关资源
        最近更新 更多