JPA中DomesticViolence、Person、PersonState三实体关联映射咨询
修正JPA实体映射以匹配业务需求
你的核心问题是误用了@ManyToMany注解——因为业务要求每对DomesticViolence-Person关联对应多个PersonState,而@ManyToMany的中间表只能存储关联关系,无法承载额外的多状态数据。正确的做法是取消@ManyToMany,将PersonState作为关联DomesticViolence和Person的实体,通过双向@OneToMany/@ManyToOne来映射关系。
问题分析
原代码中@ManyToMany生成的中间表domestic-violence-person只能记录“某DomesticViolence关联某Person”,但无法为这对关联存储多个状态(如drunk、drugged)。而PersonState本身就是关联关系的载体,同时需要保存状态信息,因此需要调整实体映射逻辑。
修正后的实体代码
1. DomesticViolence实体
移除@ManyToMany,改为关联PersonState的@OneToMany:
@Entity @Table(name = "domestic_violence") @Getter @Setter public class DomesticViolence { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 关联该家暴事件下的所有人员状态记录 @OneToMany(mappedBy = "domesticViolence", cascade = CascadeType.ALL, orphanRemoval = true) private Set<PersonState> personStates = new HashSet<>(); // 辅助方法:快速添加人员与状态的关联 public void addPersonState(Person person, String state) { PersonState personState = new PersonState(); personState.setDomesticViolence(this); personState.setPerson(person); personState.setState(state); personStates.add(personState); person.getPersonStates().add(personState); } }
2. Person实体
同样移除@ManyToMany,改为关联PersonState的@OneToMany:
@Entity @Table(name = "person") @Getter @Setter public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 关联该人员参与的所有家暴事件状态记录 @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true) private Set<PersonState> personStates = new HashSet<>(); }
3. PersonState实体(两种主键方案)
PersonState作为关联实体,需要保证DomesticViolence-Person-State的组合唯一,提供两种实现方案:
方案一:复合主键(推荐,贴合业务唯一性)
先定义复合主键类:
@Embeddable @Getter @Setter @EqualsAndHashCode public class PersonStateId implements Serializable { @Column(name = "domestic_violence_id") private Long domesticViolenceId; @Column(name = "person_id") private Long personId; @Column(name = "state") private String state; }
然后实现PersonState实体:
@Entity @Table(name = "person_state") @Getter @Setter public class PersonState { @EmbeddedId private PersonStateId id; @ManyToOne(fetch = FetchType.LAZY, optional = false) @MapsId("domesticViolenceId") @JoinColumn(name = "domestic_violence_id") private DomesticViolence domesticViolence; @ManyToOne(fetch = FetchType.LAZY, optional = false) @MapsId("personId") @JoinColumn(name = "person_id") private Person person; }
方案二:自增主键+唯一约束
如果不想使用复合主键,可通过唯一约束保证业务唯一性:
@Entity @Table(name = "person_state", uniqueConstraints = { @UniqueConstraint(columnNames = {"domestic_violence_id", "person_id", "state"}) }) @Getter @Setter public class PersonState { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "domestic_violence_id") private DomesticViolence domesticViolence; @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "person_id") private Person person; @Column(nullable = false) private String state; // 存储drunk、drugged、sober等状态值 }
额外注意点
- 原代码中
peopele是拼写错误,应为people; - 使用
cascade = CascadeType.ALL和orphanRemoval = true可以简化关联数据的增删操作; FetchType.LAZY是默认值,可避免不必要的关联数据加载,提升性能。
内容的提问来源于stack exchange,提问作者HDonev
相关产品推荐
相关产品推荐

