Spring Data JPA中EmbeddedId含自增字段,保存后无法获取ID值
复合主键中自动生成ID保存后无法获取的解决办法
我有一个Customer实体,主键是复合键,其中的id字段为自动生成值。实体类及嵌入式主键类代码如下:
@Entity @AllArgsConstructor @Builder @Setter @Getter public class Customer { @EmbeddedId private CustomerId customerId; @Size(max = 255) @Column(name = "first_name") private String firstName; }
@Embeddable @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode public class CustomerId implements Serializable { private static final long serialVersionUID = 8048935613697415032L; @NotNull @Column(name = "account_id", nullable = false) private Long accountId; @NotNull @Column(name = "id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; }
使用以下代码保存Customer时,数据库成功写入记录,但无法获取saved.getCustomerId().getId()的值(返回null),尝试过EmbeddedId和IdClass均无效,求解决办法:
@PostMapping public ResponseEntity create(@RequestBody Customer sc, @RequestParam long account_id) { sc.setCustomerId(CustomerId.builder().accountId(account_id).build()); Customer saved = customerRepository.saveAndFlush(sc); return ResponseEntity.ok(saved); }
问题原因
JPA规范中,嵌入式主键(@EmbeddedId)内的字段不支持直接使用@GeneratedValue,这是因为嵌入式主键被视为一个整体,JPA不会单独对其中的字段进行自动生成值的回填操作。
解决办法
方案1:拆分主键字段,使用@IdClass+单独标注自动生成ID
把自动生成的id从嵌入式主键中拆分出来,作为实体的单独主键字段,同时保留accountId作为另一个主键字段,使用@IdClass定义复合主键:
- 定义主键类
CustomerId(移除@GeneratedValue):
@Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode public class CustomerId implements Serializable { private static final long serialVersionUID = 8048935613697415032L; private Long accountId; private Long id; }
- 修改
Customer实体类:
@Entity @AllArgsConstructor @Builder @Setter @Getter @IdClass(CustomerId.class) public class Customer { @Id @NotNull @Column(name = "account_id", nullable = false) private Long accountId; @Id @NotNull @Column(name = "id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Size(max = 255) @Column(name = "first_name") private String firstName; }
- 调整保存逻辑:
@PostMapping public ResponseEntity create(@RequestBody Customer sc, @RequestParam long account_id) { sc.setAccountId(account_id); // id由数据库自动生成,无需手动设置 Customer saved = customerRepository.saveAndFlush(sc); return ResponseEntity.ok(saved); }
方案2:使用@MapsId关联单独的自动生成字段(保留嵌入式主键结构)
如果希望保留嵌入式主键的结构,可以通过单独的字段承载自动生成值,再用@MapsId映射到嵌入式主键中:
- 修改
CustomerId(移除@GeneratedValue):
@Embeddable @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode public class CustomerId implements Serializable { private static final long serialVersionUID = 8048935613697415032L; @NotNull @Column(name = "account_id", nullable = false) private Long accountId; @NotNull @Column(name = "id", nullable = false) private Long id; }
- 修改
Customer实体类:
@Entity @AllArgsConstructor @Builder @Setter @Getter public class Customer { @EmbeddedId private CustomerId customerId; // 单独定义自动生成的id字段,映射到嵌入式主键的id属性 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", insertable = false, updatable = false) @MapsId("id") private Long generatedId; @Size(max = 255) @Column(name = "first_name") private String firstName; }
- 调整保存逻辑:
@PostMapping public ResponseEntity create(@RequestBody Customer sc, @RequestParam long account_id) { sc.setCustomerId(CustomerId.builder().accountId(account_id).build()); Customer saved = customerRepository.saveAndFlush(sc); // 此时saved.getCustomerId().getId()会被正确回填,也可直接获取saved.getGeneratedId() return ResponseEntity.ok(saved); }
内容的提问来源于stack exchange,提问作者Arul Pragasam
相关产品推荐
相关产品推荐

