如何在JPA中实现含复合主键表的@ManyToMany关联?
JPA多对多关联(复合主键场景)修正方案
核心问题
- Table2映射冲突:错误地给单独的
id和language字段添加@MapsId注解,与@EmbeddedId的table2Id形成重复映射,导致Hibernate解析实体时出现异常。 - 多对多关联不完整:关联Table2时仅指定了复合主键的
id字段,未包含language字段,JPA无法识别完整的关联条件。 - 冗余字段:Table1中的
table2Id属于冗余字段,多对多关系通过中间表维护,无需单独存储该外键。
修正后的代码实现
1. 复合主键类Table2Id
@Embeddable data class Table2Id( val id: Long, val language: Languages // 确保枚举类Languages已正确定义 ) : Serializable
Kotlin的data class会自动生成equals、hashCode方法,满足JPA对嵌入式主键的序列化要求。
2. Table2实体类
@Entity @Table(name = "table_2") class Table2( @EmbeddedId val table2Id: Table2Id, @Column val title: String, @Column val tag: String ) { // 通过嵌入式主键间接暴露id和language,避免重复映射 val id: Long get() = table2Id.id val language: Languages get() = table2Id.language }
3. Table1实体类
@Entity @Table(name = "table_1") class Table1( @Column val foo: Double, @Column val bar: Double, @ManyToMany @JoinTable( name = "table_1_table_2_link", joinColumns = [JoinColumn(name = "table_1_id", referencedColumnName = "id")], // 必须指定复合主键的全部字段,对应中间表和Table2的主键属性 inverseJoinColumns = [ JoinColumn(name = "table_2_id", referencedColumnName = "id"), JoinColumn(name = "table_2_language", referencedColumnName = "language") ] ) val table2: MutableList<Table2> = mutableListOf() // 用可变集合方便后续关联操作 ) { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = 0 }
注意事项
- 中间表
table_1_table_2_link需要创建三个字段:table_1_id、table_2_id、table_2_language,这三个字段共同构成中间表的复合主键。 - 如果需要双向关联,可在Table2中添加如下代码:
@ManyToMany(mappedBy = "table2") val table1: MutableList<Table1> = mutableListOf()
内容的提问来源于stack exchange,提问作者four-eyes
相关产品推荐
相关产品推荐

