JPA+Hibernate实现复合主键自动生成时Post请求报错如何解决?
错误原因与解决方案
错误产生原因
- Hibernate 6以下版本原生不支持
@IdClass复合主键场景下多个@Id字段同时使用@GeneratedValue自动生成值,在封装主键对象时会出现类型映射错误,你遇到的报错就是Hibernate将主键生成器的内部临时对象直接赋值给ProjetoId里的Long类型ra字段,导致类型不匹配。 - PostgreSQL单表仅支持1个
IDENTITY类型的自增列,你给两个主键字段都配置GenerationType.IDENTITY策略本身不符合PostgreSQL的底层限制,无法直接实现。 - 你的
ProjetoId主键类没有加public访问修饰符,默认是包访问权限,Hibernate反射操作该类字段时也可能触发访问权限问题。
修复方案
分两种场景选择适配方案:
场景1:必须使用复合主键,两个字段都需要自动生成
- 首先修正
ProjetoId的访问修饰符为public:
@Data @EqualsAndHashCode(onlyExplicitlyIncluded = true) @AllArgsConstructor @NoArgsConstructor public class ProjetoId implements Serializable{ Long idProjeto; Long ra; }
- 替换生成策略为序列生成(PostgreSQL支持多序列独立生成值),修改实体类两个主键字段的注解:
@EqualsAndHashCode.Include @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "projeto_id_seq") @SequenceGenerator(name = "projeto_id_seq", sequenceName = "projeto_id_seq", allocationSize = 1) @Column(name = "id_projeto") private Long idProjeto; // 中间其他业务字段省略 @EqualsAndHashCode.Include @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "projeto_ra_seq") @SequenceGenerator(name = "projeto_ra_seq", sequenceName = "projeto_ra_seq", allocationSize = 1) @Column(name = "ra") private Long ra;
- 版本适配:
- 若使用Hibernate 6+版本,上述配置完成后即可正常运行,Hibernate会自动处理两个序列的生成并组装为
ProjetoId对象 - 若使用Hibernate 5及更低版本,需要自定义复合主键生成器,实现
IdentifierGenerator接口,在生成方法中分别调用两个序列获取值,封装为ProjetoId对象返回后配置到@GeneratedValue的generator属性中。
- 若使用Hibernate 6+版本,上述配置完成后即可正常运行,Hibernate会自动处理两个序列的生成并组装为
场景2:仅需保证两个字段唯一,无强制复合主键要求
更简单的方案是放弃复合主键,仅将idProjeto设为唯一主键,ra字段添加唯一约束即可,避免复合主键的各种持久化坑:
// 去掉类上的@IdClass注解 @Entity @Data @EqualsAndHashCode(onlyExplicitlyIncluded = true) @Table(name = "projeto", schema = "public") public class Projeto { @EqualsAndHashCode.Include @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id_projeto") private Long idProjeto; // 其他业务字段省略 @EqualsAndHashCode.Include @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "projeto_ra_seq") @SequenceGenerator(name = "projeto_ra_seq", sequenceName = "projeto_ra_seq", allocationSize = 1) @Column(name = "ra", unique = true, nullable = false) private Long ra; // 剩余业务方法省略 }
内容的提问来源于stack exchange,提问作者Gecko
相关产品推荐
相关产品推荐

