JaVers能否追踪含Observable Properties的JavaFX模型类变更?
我之前也碰到过一模一样的问题,核心原因就是JaVers依赖的GSON在反序列化时,会尝试用无参构造实例化你的ObjectProperty字段,但JavaFX的SimpleObjectProperty并没有公开的无参构造函数,所以才抛出了你看到的那个RuntimeException。下面给你两个可行的解决方案,优先推荐第二个,实现起来更简单:
方案一:给GSON添加自定义TypeAdapter处理ObjectProperty
你可以写一个GSON的类型适配器,专门处理JavaFX的ObjectProperty类型,序列化时取它的实际值,反序列化时创建带值的SimpleObjectProperty实例。
首先写一个通用的TypeAdapterFactory:
public class ObjectPropertyTypeAdapterFactory implements TypeAdapterFactory { @SuppressWarnings("unchecked") @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { // 只处理ObjectProperty类型 if (!ObjectProperty.class.isAssignableFrom(typeToken.getRawType())) { return null; } // 获取泛型参数类型(比如ObjectProperty<String>里的String) Type genericType = ((ParameterizedType) typeToken.getType()).getActualTypeArguments()[0]; TypeAdapter<?> delegate = gson.getAdapter(TypeToken.get(genericType)); return (TypeAdapter<T>) new TypeAdapter<ObjectProperty<?>>() { @Override public void write(JsonWriter out, ObjectProperty<?> value) throws IOException { if (value == null) { out.nullValue(); return; } // 序列化时输出实际值 delegate.write(out, value.getValue()); } @Override public ObjectProperty<?> read(JsonReader in) throws IOException { // 反序列化时读取值并创建SimpleObjectProperty Object value = delegate.read(in); // 注意:如果需要绑定到Demo实例,这里没法自动获取,若不需要绑定功能,用这个构造就行 return new SimpleObjectProperty<>(value); } }; } }
然后在创建Javers实例时,把这个工厂注册到GSON中:
Gson customGson = new GsonBuilder() .registerTypeAdapterFactory(new ObjectPropertyTypeAdapterFactory()) .create(); Javers javers = JaversBuilder.javers() .withMappingStyle(MappingStyle.BEAN) .withGson(customGson) .build();
这个方案的缺点是:如果你的ObjectProperty需要绑定到Demo实例(比如支持JavaFX的属性变更监听),通用适配器没法自动传入Demo实例,需要额外处理。
方案二:用@JsonIgnore忽略ObjectProperty字段(推荐)
这个方案更简单,只需要在你的ObjectProperty字段上添加GSON的@JsonIgnore注解,让GSON跳过这个字段,转而通过你的getter/setter来处理实际的String值:
import com.google.gson.annotations.JsonIgnore; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import org.javers.core.metamodel.annotation.Id; class Demo { @JsonIgnore // 让GSON忽略这个字段 private ObjectProperty<String> someProp = new SimpleObjectProperty<>(this, "someProp"); public Demo(String someProp) { this.someProp.setValue(someProp); } public ObjectProperty<String> somePropProperty() { return someProp; } @Id public String getSomeProp() { return someProp.getValue(); } public void setSomeProp(String someProp) { this.someProp.setValue(someProp); } }
这样GSON在序列化/反序列化Demo对象时,只会处理getSomeProp()和setSomeProp()对应的String属性,完全不会碰ObjectProperty字段,自然就不会触发无参构造的异常了。而且这个方案完全不影响JavaFX的绑定功能,somePropProperty()依然能正常返回属性供UI绑定使用。
之后你再运行原来的Javers代码,应该就能正常查询到变更记录了。
内容的提问来源于stack exchange,提问作者Joe

