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
,但这只会导致已经检测到的更改被延迟并且没有启用任何新的检测。其他跟踪类型(ATTRIBUTE
和 OBJECT
)要求实体实现特定接口 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();
我在 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
,但这只会导致已经检测到的更改被延迟并且没有启用任何新的检测。其他跟踪类型(ATTRIBUTE
和 OBJECT
)要求实体实现特定接口 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();