使用Hibernate Reactive持久化复合ID时出现字段访问错误
问题分析与解决方案
错误栈的核心信息显示:Can not set java.lang.String field myPackage.MyTableEntity.code2 to myPackage.MyTableEntity,说明Hibernate在处理复合ID的反射逻辑时,错误地将实体对象本身赋值给了code2字段(String类型),导致类型不匹配异常。结合你的代码场景,以下是针对性的解决方案:
1. 调整实体类ID字段的顺序与连续性
Hibernate处理@IdClass时,会按照ID类中字段的顺序匹配实体类的ID字段。如果实体类中ID字段被非ID字段打断,可能引发反射字段匹配错误。将非ID字段num移到所有ID字段之后:
@Table(name = "MY_TABLE") @Entity @IdClass(MyTableId.class) @Data @Builder @NoArgsConstructor @AllArgsConstructor public class MyTableEntity { @Id @Column(name = "CODE_1") private String code1; @Id @Column(name = "CODE_2") private String code2; @Id @Column(name = "DATE") private LocalDateTime date; @Column(name = "NUM") private Long num; }
2. 显式在事务上下文执行持久化操作
Hibernate Reactive的持久化操作需要在事务中完成,虽然部分场景下withSession能工作,但显式使用withTransaction可以避免上下文缺失导致的异常:
@Repository @AllArgsConstructor public class MyTableRepository { private Stage.SessionFactory sessionFactory; public Mono<Void> save(MyTableEntity myTableEntity) { return Mono.fromCompletionStage( sessionFactory.withTransaction((session, transaction) -> session.persist(myTableEntity) ) ); } // 其他查询方法... }
3. 移除Lombok注解,手动实现核心方法
Lombok生成的@Data可能与Hibernate的反射逻辑存在冲突,手动实现getter/setter、equals和hashCode(仅基于ID字段)可以排除此类问题:
实体类手动实现
import jakarta.persistence.*; import java.time.LocalDateTime; import java.util.Objects; @Table(name = "MY_TABLE") @Entity @IdClass(MyTableId.class) public class MyTableEntity { @Id @Column(name = "CODE_1") private String code1; @Id @Column(name = "CODE_2") private String code2; @Id @Column(name = "DATE") private LocalDateTime date; @Column(name = "NUM") private Long num; // 无参、全参构造方法 public MyTableEntity() {} public MyTableEntity(String code1, String code2, LocalDateTime date, Long num) { this.code1 = code1; this.code2 = code2; this.date = date; this.num = num; } // getter/setter public String getCode1() { return code1; } public void setCode1(String code1) { this.code1 = code1; } public String getCode2() { return code2; } public void setCode2(String code2) { this.code2 = code2; } public LocalDateTime getDate() { return date; } public void setDate(LocalDateTime date) { this.date = date; } public Long getNum() { return num; } public void setNum(Long num) { this.num = num; } // equals/hashCode仅基于ID字段 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyTableEntity that = (MyTableEntity) o; return Objects.equals(code1, that.code1) && Objects.equals(code2, that.code2) && Objects.equals(date, that.date); } @Override public int hashCode() { return Objects.hash(code1, code2, date); } }
复合ID类手动实现
import java.io.Serializable; import java.time.LocalDateTime; import java.util.Objects; public class MyTableId implements Serializable { private String code1; private String code2; private LocalDateTime date; public MyTableId() {} public MyTableId(String code1, String code2, LocalDateTime date) { this.code1 = code1; this.code2 = code2; this.date = date; } // getter/setter public String getCode1() { return code1; } public void setCode1(String code1) { this.code1 = code1; } public String getCode2() { return code2; } public void setCode2(String code2) { this.code2 = code2; } public LocalDateTime getDate() { return date; } public void setDate(LocalDateTime date) { this.date = date; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyTableId myTableId = (MyTableId) o; return Objects.equals(code1, myTableId.code1) && Objects.equals(code2, myTableId.code2) && Objects.equals(date, myTableId.date); } @Override public int hashCode() { return Objects.hash(code1, code2, date); } }
4. 升级Hibernate Reactive版本
你使用的2.0.6.Final版本存在部分@IdClass相关的已知bug,升级到最新的2.x稳定版本(如2.2.0.Final)可以修复此类问题。
内容的提问来源于stack exchange,提问作者MiKa
相关产品推荐
相关产品推荐

