Hibernate多实体外键构建用户-部门-角色关联表实现问询
如何用Hibernate实现用户-部门-角色三元关联表
首先得说,你现在的代码思路有点问题——因为你要实现的是用户、部门、角色三者的关联(每个用户在某个部门下对应一个角色),这属于三元关联,不是普通的多对多关系,所以用@ManyToOne配合@JoinTable的方式是行不通的。下面我给你两种可行的实现方案,推荐第一种,因为扩展性更好。
方案一:创建独立的中间关联实体(推荐)
这种方式把关联表user_department_role做成一个完整的实体类,这样不仅能维护三个实体的关联,后续还能轻松添加额外字段(比如关联生效时间、备注等)。
1. 先定义三个基础实体的代码
User实体
@Entity @Table(name = "User") public class User { @Id @Column(name = "User_id") private Long userId; @Column(name = "User_name") private String userName; // 关联到中间实体,mappedBy指向中间实体中对User的引用 @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private Set<UserDepartmentRole> userDepartmentRoles = new HashSet<>(); // 构造方法、getter、setter省略 }
Department实体
@Entity @Table(name = "Department") public class Department { @Id @Column(name = "Departmant_id") // 注意你原表的拼写是Departmant,这里保持一致 private Long departmentId; @Column(name = "Departmant") private String departmentName; @OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true) private Set<UserDepartmentRole> userDepartmentRoles = new HashSet<>(); // 构造方法、getter、setter省略 }
Role实体
@Entity @Table(name = "Role") public class Role { @Id @Column(name = "Role_id") private Long roleId; @Column(name = "Role") private String roleName; @OneToMany(mappedBy = "role", cascade = CascadeType.ALL, orphanRemoval = true) private Set<UserDepartmentRole> userDepartmentRoles = new HashSet<>(); // 构造方法、getter、setter省略 }
2. 定义中间关联实体和联合主键
因为user_department_role的主键是User_id、Departmant_id、Role_id的联合,所以先创建一个可嵌入的主键类:
@Embeddable public class UserDepartmentRoleId implements Serializable { @Column(name = "User_id") private Long userId; @Column(name = "Departmant_id") private Long departmentId; @Column(name = "Role_id") private Long roleId; // 必须提供无参构造 public UserDepartmentRoleId() {} public UserDepartmentRoleId(Long userId, Long departmentId, Long roleId) { this.userId = userId; this.departmentId = departmentId; this.roleId = roleId; } // 必须重写equals和hashCode,用于Hibernate识别主键 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserDepartmentRoleId that = (UserDepartmentRoleId) o; return Objects.equals(userId, that.userId) && Objects.equals(departmentId, that.departmentId) && Objects.equals(roleId, that.roleId); } @Override public int hashCode() { return Objects.hash(userId, departmentId, roleId); } // getter、setter省略 }
然后是中间关联实体本身:
@Entity @Table(name = "user_department_role") public class UserDepartmentRole { @EmbeddedId private UserDepartmentRoleId id; // 关联到User,@MapsId指定对应联合主键中的userId字段 @ManyToOne @MapsId("userId") @JoinColumn(name = "User_id", insertable = false, updatable = false) private User user; // 关联到Department @ManyToOne @MapsId("departmentId") @JoinColumn(name = "Departmant_id", insertable = false, updatable = false) private Department department; // 关联到Role @ManyToOne @MapsId("roleId") @JoinColumn(name = "Role_id", insertable = false, updatable = false) private Role role; // 无参构造 public UserDepartmentRole() {} // 方便创建关联的构造方法 public UserDepartmentRole(User user, Department department, Role role) { this.user = user; this.department = department; this.role = role; this.id = new UserDepartmentRoleId(user.getUserId(), department.getDepartmentId(), role.getRoleId()); } // getter、setter省略 }
3. 使用示例
比如给Jennifer添加生产部门的经理角色:
// 从数据库获取实例 User jennifer = entityManager.find(User.class, 1L); Department production = entityManager.find(Department.class, 1L); Role manager = entityManager.find(Role.class, 1L); // 创建关联关系 UserDepartmentRole association = new UserDepartmentRole(jennifer, production, manager); jennifer.getUserDepartmentRoles().add(association); production.getUserDepartmentRoles().add(association); manager.getUserDepartmentRoles().add(association); // 保存到数据库 entityManager.persist(association);
方案二:不创建中间实体(不推荐,扩展性差)
如果你实在不想创建中间实体,可以用@ManyToMany配合@JoinTable的扩展,但这种方式没法直接操作角色关联,也不方便后续加字段:
修改User实体:
@Entity @Table(name = "User") public class User { @Id @Column(name = "User_id") private Long userId; @Column(name = "User_name") private String userName; @ManyToMany @JoinTable( name = "user_department_role", joinColumns = { @JoinColumn(name = "User_id", referencedColumnName = "User_id") }, inverseJoinColumns = { @JoinColumn(name = "Departmant_id", referencedColumnName = "Departmant_id"), @JoinColumn(name = "Role_id", referencedColumnName = "Role_id") } ) private Set<Object[]> departmentRolePairs; // 这里只能存Object数组,使用起来很麻烦 }
这种方式的问题很明显:你只能拿到部门和角色的数组,没法直接映射到实体对象,操作起来非常不直观,所以强烈推荐第一种方案。
为什么你的原代码不对?
- 你用了
@ManyToOne,但User和Department是多对多关系,这个注解本身就用错了。 @JoinTable的joinColumns和inverseJoinColumns只能对应两个实体的外键,没法直接添加第三个Role的外键,所以没法实现三元关联。
内容的提问来源于stack exchange,提问作者Mert Doe
相关产品推荐
相关产品推荐

