Spring Data JPA PostgreSQL非主键serial字段save返回null问题
字段配置的insertable = false属性会告知Hibernate:该字段不需要纳入INSERT语句的写入列,值由数据库侧自动生成。但Hibernate默认仅会对@Id标注的主键字段,在插入后自动回查数据库生成的值,非主键的数据库生成字段不会触发自动回写逻辑,因此save方法返回的实体对象中该字段为null。
以下方案按推荐优先级从高到低排列:
方案1:使用Hibernate
@Generated注解(改造成本最低)该注解是Hibernate专门为数据库侧自动生成值的字段设计的,标记后Hibernate会在对应操作执行完成后,自动回查字段值填充到实体中,无需额外写查询逻辑。
直接修改字段注解配置即可:import org.hibernate.annotations.Generated; import org.hibernate.annotations.GenerationTime; @Column(name = "DEMANDE_NUMBER", updatable = false) @Generated(GenerationTime.INSERT) // 标记为插入操作后自动回读生成值 private Integer demandeNumber;原配置中的
insertable = false可以移除,@Generated(GenerationTime.INSERT)本身就会阻止字段被纳入INSERT写入逻辑,不会出现手动赋值覆盖数据库生成值的问题。如果使用Hibernate 6+版本,注解可简化为@Generated(event = GenerationTime.INSERT),效果完全一致。方案2:显式绑定序列生成器(符合JPA标准,无Hibernate特有API依赖)
直接在非主键字段加
@GeneratedValue不生效的核心原因是:JPA默认的@GeneratedValue仅对主键字段生效,非主键字段需要显式绑定对应的序列生成器,由Hibernate在插入前先调用序列获取值,直接写入INSERT语句,无需插入后回查。
配置示例如下:import jakarta.persistence.*; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; // 主键配置保持原有逻辑不变 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "DEMANDE_NUMBER", nullable = false, updatable = false) @GenericGenerator( name = "demande_number_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { // 填写该serial列实际绑定的序列名,serial列默认生成规则为「表名_列名_seq」,自定义序列填自定义名称即可 @Parameter(name = "sequence_name", value = "demande_demande_number_seq"), @Parameter(name = "increment_size", value = "1") } ) @GeneratedValue(generator = "demande_number_generator") private Integer demandeNumber;该方案的优势是值在插入前就已获取到实体中,不需要插入后额外回查,跨数据库兼容性更好。
方案3:手动刷新实体(临时兜底方案,不推荐生产使用)
如果不想修改实体注解配置,可以在save操作后手动调用refresh方法,强制从数据库拉取最新的字段值:
Demande savedDemande = demandeRepository.save(demandeToSave); // 强制同步数据库最新值到实体对象 demandeRepository.refresh(savedDemande); // 此时savedDemande.getDemandeNumber()可获取到正确生成的值该方案会额外产生一次数据库查询,存在明显性能损耗,仅适合临时调试场景使用,需要确保Repository继承了
JpaRepository接口才能使用refresh方法。
注意:不要尝试仅通过
@ColumnDefault注解实现该需求,该注解仅在Hibernate自动建表时为列添加默认值约束,不会触发插入后的字段值回读逻辑,无法解决返回null的问题。
内容的提问来源于stack exchange,提问作者James

