@MappedSuperclass父类与Role类多对多映射问题求助
解决方案
方案1:在Role中分别与每个子类建立多对多关联
既然@MappedSuperclass只是用来共享映射元数据的非实体类,没法直接和它建立关联,你可以在Role类里为每个User子类单独定义多对多关系,示例代码如下:
@Entity public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToMany @JoinTable( name = "role_merchant", joinColumns = @JoinColumn(name = "role_id"), inverseJoinColumns = @JoinColumn(name = "merchant_id") ) private Set<Merchant> merchants = new HashSet<>(); @ManyToMany @JoinTable( name = "role_buyer", joinColumns = @JoinColumn(name = "role_id"), inverseJoinColumns = @JoinColumn(name = "buyer_id") ) private Set<Buyer> buyers = new HashSet<>(); // getter、setter及业务方法 }
这个方案不需要改动原有的父类结构,也不会生成父类表,每个子类和Role的关联对应单独的中间表。唯一的缺点是如果后续新增User子类,需要同步在Role类里添加对应的多对多关联代码,有一定的重复工作。
方案2:用单表继承复用父类映射(无独立父类表)
如果不想单独生成父类表,可以把User改成抽象实体,采用单表继承策略,所有子类的数据都存在同一张用户表中,通过鉴别器列区分不同类型的用户。示例代码:
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "user_type", discriminatorType = DiscriminatorType.STRING) public abstract class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; // 其他共享字段 } @Entity @DiscriminatorValue("MERCHANT") public class Merchant extends User { // Merchant专属字段 } @Entity @DiscriminatorValue("BUYER") public class Buyer extends User { // Buyer专属字段 }
之后Role就可以直接和User父类建立多对多关联:
@Entity public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToMany @JoinTable( name = "role_user", joinColumns = @JoinColumn(name = "role_id"), inverseJoinColumns = @JoinColumn(name = "user_id") ) private Set<User> users = new HashSet<>(); // getter、setter及业务方法 }
这种方式只需要维护一组多对多关系,新增子类时不用修改Role类,且只有一张用户表,没有独立的父类表。但如果子类的专属字段较多,用户表会存在不少空字段,需要根据业务场景权衡。
方案3:通过中间实体间接关联(完全保留原有结构)
定义一个中间实体UserRole,它分别关联Role和各个User子类的ID,同时记录用户类型,以此实现间接关联:
@Entity public class UserRole { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn(name = "role_id") private Role role; @Column(name = "user_type") private String userType; // 比如"MERCHANT"、"BUYER" @Column(name = "user_id") private Long userId; // 可添加业务方法,根据userType和userId获取对应的User子类实例 }
然后在Role类中通过@OneToMany关联这个中间实体:
@Entity public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @OneToMany(mappedBy = "role", cascade = CascadeType.ALL, orphanRemoval = true) private Set<UserRole> userRoles = new HashSet<>(); // 业务方法示例:添加Merchant到当前角色 public void addMerchant(Merchant merchant) { UserRole userRole = new UserRole(); userRole.setRole(this); userRole.setUserType("MERCHANT"); userRole.setUserId(merchant.getId()); userRoles.add(userRole); } // 其他添加方法及查询逻辑 }
这个方案完全不需要改动原有的User父类和子类结构,也不会生成额外的父类表。缺点是需要自己处理关联的查询逻辑,比如根据userType和userId查询对应的子类实例,代码量会稍多,但灵活性最高。
内容的提问来源于stack exchange,提问作者קובי טראב
相关产品推荐
相关产品推荐

