You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用IdClass时无法分配自动生成值的技术问题求助

问题描述

我有一个采用复合主键的Customer实体,复合主键中的id字段为自动生成值。实体类与主键类代码如下:

Customer 实体类

@Entity
@AllArgsConstructor
@Builder
@Setter
@Getter
@IdClass(CustomerId.class)
public class Customer {
 
  @NotNull
  @Column(name = "account_id", nullable = false)
  private Long accountId;

  @NotNull
  @Column(name = "id", nullable = false)
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;


  @Size(max = 255)
  @Column(name = "first_name")
  private String firstName;
}

CustomerId 主键类

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class CustomerId implements Serializable {
  private static final long serialVersionUID = 8048935613697415032L;
  private Long accountId;

  private Long id;
}

当使用以下代码保存Customer时:

@PostMapping
public ResponseEntity create(@RequestBody Customer sc,
                             @RequestParam long account_id) {
  Customer saved = customerRepository.saveAndFlush(sc);
  return ResponseEntity.ok(saved);
}

出现错误:

org.springframework.orm.jpa.JpaSystemException: Could not set field value [POST_INSERT_INDICATOR] value by reflection : [class com.demo.service.entity.core.CustomerId.id] setter of com.demo.service.entity.core.CusotmerId.id;


问题原因

这个错误的核心是JPA的@IdClass机制不支持复合主键中包含自动生成字段。使用@IdClass时,JPA要求所有主键字段在持久化前必须有明确值,但@GeneratedValue(strategy = GenerationType.IDENTITY)是在数据插入数据库后才生成值,两者存在冲突。Hibernate处理时会尝试给主键类的id字段设置POST_INSERT_INDICATOR标记,但主键类的id字段类型为Long,无法接收该特殊标记,从而抛出反射赋值异常。


解决方案

方案1:改用@EmbeddedId替代@IdClass

@EmbeddedId支持复合主键包含自动生成字段,调整代码如下:

  1. 修改主键类,添加@Embeddable注解,并将@GeneratedValue移到主键类的id字段上:
@Embeddable
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class CustomerId implements Serializable {
  private static final long serialVersionUID = 8048935613697415032L;
  
  private Long accountId;

  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
}
  1. 修改Customer实体类,替换@IdClass为@EmbeddedId,并调整字段定义:
@Entity
@AllArgsConstructor
@Builder
@Setter
@Getter
public class Customer {
  @EmbeddedId
  private CustomerId id;

  @Size(max = 255)
  @Column(name = "first_name")
  private String firstName;
}
  1. 调整保存接口代码,正确设置复合主键的accountId:
@PostMapping
public ResponseEntity create(@RequestBody Customer sc,
                             @RequestParam long account_id) {
  // 初始化主键对象并设置accountId
  if(sc.getId() == null) {
    sc.setId(new CustomerId());
  }
  sc.getId().setAccountId(account_id);
  Customer saved = customerRepository.saveAndFlush(sc);
  return ResponseEntity.ok(saved);
}

方案2:调整主键策略(需评估业务场景)

如果不想使用@EmbeddedId,可以重新设计主键:将accountId与某个非自动生成的业务字段作为复合主键,把id改为普通的自动生成字段(不再作为主键的一部分)。这种方式会改变实体的主键逻辑,需要结合业务场景判断是否适用。


内容的提问来源于stack exchange,提问作者Arul Pragasam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 02:15:38