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

Javers findChanges未捕获嵌套值对象更新问题求助

问题描述

持久化不含exclusions的Foo实体后,更新实体添加exclusion值时出现异常:Javers的findChanges方法未捕获到更新操作,但快照和影子查询能看到版本变化,日志显示无变更,可原始实体与更新后实体的哈希值确实不同。

相关代码

实体类 Foo

@NoArgsConstructor
@AllArgsConstructor
@ToString(onlyExplicitlyIncluded = true)
@Builder(toBuilder = true)
@Getter
@Setter
@Entity
public class Foo {
    @ToString.Include
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @ToString.Include
    @Valid
    @NotNull
    @AttributeOverride(
        name = "origin.elementType",
        column = @Column(name = "inclusion_origin_element_type", nullable = false)
    )
    @AttributeOverride(name = "origin.values", column = @Column(name = "inclusion_origin_value", length = 64))
    @AttributeOverride(name = "destination.elementType", column = @Column(name = "inclusion_destination_element_type"))
    @AttributeOverride(name = "destination.values", column = @Column(name = "inclusion_destination_value", length = 64))
    @AttributeOverride(name = "bothDirections", column = @Column(name = "inclusion_both_direction", nullable = false))
    @Embedded
    private CompositionDetails inclusions;

    @ToString.Include
    @Valid
    @Nullable
    @AttributeOverride(name = "origin.elementType", column = @Column(name = "exclusion_origin_element_type"))
    @AttributeOverride(name = "origin.values", column = @Column(name = "exclusion_origin_value", length = 64))
    @AttributeOverride(name = "destination.elementType", column = @Column(name = "exclusion_destination_element_type"))
    @AttributeOverride(name = "destination.values", column = @Column(name = "exclusion_destination_value", length = 64))
    @AttributeOverride(name = "bothDirections", column = @Column(name = "exclusion_both_direction"))
    @Embedded
    private CompositionDetails exclusions;

    //......
}

可嵌入类 CompositionDetails

@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@Embeddable
public class CompositionDetails {

    @Valid
    @NotNull
    @Embedded
    private CompositionElement origin;

    @Valid
    @Nullable
    @Embedded
    private CompositionElement destination;

    @Builder.Default
    @Nullable
    private Boolean bothDirections = Boolean.FALSE;
}

可嵌入类 CompositionElement

@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@Embeddable
public class CompositionElement {

    @NotNull
    @Column(nullable = false, length = 16)
    @Enumerated(EnumType.STRING)
    private ElementType elementType;

    @Builder.Default
    @NotEmpty
    @ElementCollection(fetch = FetchType.EAGER)
    private List<String> values = new ArrayList<>();
}

异常现象

  • 调用 javers.findChanges(QueryBuilder.byInstanceId(id, clazz).withChildValueObjects(true).build()) 仅返回初始的“创建”变更,未捕获到更新操作
  • 调用 javers.findSnapshots(QueryBuilder.byInstanceId(id, clazz).withChildValueObjects(true).build()) 能看到两个版本,且exclusion已更新
  • 调用 javers.findShadows(QueryBuilder.byInstanceId(id, clazz).withChildValueObjects(true).build()) 显示两个版本,分别为无exclusion的基础版本和含exclusion的新版本

更新日志

org.javers.core.Javers                   : Commit(id:11.00, snapshots:3, author:unknown, changes -), done in 5 ms (diff:4 ms, persist:1 ms)

环境信息

  • JDK:21 (OpenJDK / Eclipse Temurin)
  • spring-boot-starter-parent:3.3.4
  • javers-spring-boot-starter-sql:7.6.2

问题原因及解决办法

  1. 关闭null属性忽略配置
    Javers默认可能开启了ignore-null-properties,导致属性从null变为非null的变更被忽略。在配置文件中显式关闭该选项:
javers:
  diff:
    ignore-null-properties: false
  1. 给值对象添加属性比较注解
    虽然Javers会自动遍历属性比较,但为了避免逻辑歧义,给CompositionDetails和CompositionElement添加@EqualsAndHashCode注解,确保属性级比较逻辑正确:
// 给CompositionDetails添加@EqualsAndHashCode
@EqualsAndHashCode
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@Embeddable
public class CompositionDetails {
    // 原有代码
}

// 给CompositionElement添加@EqualsAndHashCode
@EqualsAndHashCode
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@Embeddable
public class CompositionElement {
    // 原有代码
}
  1. 验证Javers提交触发逻辑
    如果用Spring Boot Starter,确保Repository接口添加@JaversSpringDataAuditable注解,或者继承JaversSpringDataAuditableRepository,保证实体更新时Javers自动触发提交:
@JaversSpringDataAuditable
public interface FooRepository extends JpaRepository<Foo, Integer> {
}
  1. 排查版本兼容性
    当前Javers 7.6.2与Spring Boot 3.3.4可能存在兼容性bug,尝试升级Javers到最新稳定版本(如7.10.0)验证问题是否修复。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:51:07