EclipseLink 不跟踪对已转换包装器属性的更改

EclipseLink not tracking changes to converted wrapper attributes

我在 Java SE 应用程序中的一个实体 classes 中遇到 EclipseLink 更改跟踪问题。我正在使用 Java 8,EclipseLink 3.0.2 和 HyperSQL 2.6.1 提供的 JPA 3.0。到目前为止,我一直保持我的实现提供者独立,因此切换 JPA 提供者是一种选择,尽管不是可取的。

此特定实体 class 具有类型 OverriddenValue 的约 10 个属性,每个属性都是 1. 对特定全局配置值的引用,以及 2. 可选自定义的包装器将覆盖全局值(如果存在)的值。

public class OverriddenValue<T> {

    @Nullable
    private T customValue;
    private final OverridableValue<?, T> globalConfigValue;

    [...]

}

此class包含getter和setter逻辑,这使得将自定义值直接存储在实体中非常不方便。所以我需要包装这些自定义值。

我的实体 class 中的每个 OverriddenValue 我都使用唯一的 AttributeConverter 标记为 @Convert。所有这些 AttributeConverters 只是 return Java -> DB 映射的自定义值,对于 DB -> Java 映射,它们使用正确的全局配置重建对象 OverridableValue.这是因为我需要对 OverridableValue 的引用,但我没有将 OverriddenValue 实现为 @Embeddable - 我要么必须保留全局配置值的 ID,要么使它是短暂的,我认为那太不方便了。此外,每个 OverriddenValue 实际上只需要数据库中的一列来存储其自定义值或空值,因此 @Convert 应该可以胜任。

我的问题是 EclipseLink 不检测和保存对这些对象的更改。在此实体的托管实例中,将在下一次调用 EntityManager#flush 时自动检测并保留对基本字符串属性的更改,但不会更改 OverriddenValue 的自定义值,并且这些数据库中的列将保持原样。

我查看了 EclipseLink 的更改跟踪是如何工作的,发现有人说它使用 .hashCode() 和 .equals() 来确定属性是否已更改。所以我在 OverriddenValue class 中手动实现了这些,但它们一定是错误的,因为仍然没有检测到更改。

暂时放弃提供商独立性,我尝试用 EclipseLink 的 @ChangeTracking 注释标记这个实体并将 ChangeTrackingType 更改为 DEFERRED,但这只会导致已经检测到的更改被延迟并且没有启用任何新的检测。其他跟踪类型(ATTRIBUTEOBJECT)要求实体实现特定接口 ChangeTracker,这似乎很有用,但我不太明白如何让它工作.

我也试过在 persistence.xml 文件中将 属性 "eclipselink.weaving.changetracking" 设置为 false,以防编织导致问题(我真的不知道了解编织)。运气不好。

作为一种可能的解决方法,我可以在应用程序关闭时手动合并所有此类实体,并强制覆盖数据库中的值。但我认为这是一种 hack,如果可能的话,我想避免它。我觉得 ORM 提供者应该能够检测包装的属性更改。有谁知道我接下来可以去哪里尝试解决这个问题?

编辑: 以下是转换器 class 的示例:

@Converter
public class FooConverter implements AttributeConverter<OverriddenValue<Integer>, Integer> {

    @Override
    default Integer convertToDatabaseColumn(OverriddenValue<Integer> attribute) {
        return attribute.getCustomValue();
    }

    @Override
    public OverriddenValue<Integer> convertToEntityAttribute(Integer dbData) {
        return Config.GLOBAL_CONFIG_VALUE.override(dbData); // Every converter references a different global variable
    }
}

方法 override 只是一个 OverriddenValue 工厂方法。

尝试将映射标记为可变:

@Entity
public class YourClass {
  ..
  @Convert("yourConverter")
  @Mutable
  private OverriddenValue value1;
  ..
}

或者,您可以修改自己的保存方法以克隆并设置 OverriddenValue 实例,当您知道其中有要保留的更改时。

  YourClass instance = em.find(id, YourClass.class);
  instance.setValue1(instance.getValue1().clone());
  instance.getValue1().setCustomValue(value)
  em.commit();