我有一个@Embeddable
类,它使用属性访问来包装另一个不能由JPA通过字段访问直接映射的对象。它看起来像这样:
@Embeddable
@Access(AccessType.PROPERTY)
public class MyWrapper {
@NotNull
@Transient
private WrappedType wrappedField;
protected MyWrapper() {
}
public MyWrapper(WrappedType wrappedField) {
this.wrappedField = wrappedField;
}
@Transient
public WrappedType getWrappedField() {
return wrappedField;
}
public void setWrappedField(WrappedType wrappedField) {
this.wrappedField = wrappedField;
}
@Column(name = "wrappedTypeColumn")
protected String getJPARepresentation() {
return wrappedField.toString();
}
protected void setJPARepresentation(String jpaRepresentation) {
wrappedField = new WrappedType(jpaRepresentation);
}
}
坚持@Entity
MyWrapper
字段的工作正常。但是当我执行查询以从数据库加载实体时,我得到NullPointerException
。堆栈跟踪和一些调试显示Eclipselink通过调用其默认构造函数创建MyWrapper
的新实例,然后调用setJPARepresentation()
方法(按预期方式)。
但现在意外发生了:堆栈跟踪显示从setter内部调用getJPARepresentation()
,当执行返回NullPointerException
时,当然会导致wrappedField.toString()
。
java.lang.NullPointerException
at MyWrapper.getJPARepresentation(MyWrapper.java:27)
at MyWrapper.setJPARepresentation(MyWrapper.java)
... 109 more
事实上,代码中显然没有调用getter,而堆栈跟踪没有显示行号,表示setter中的getter位置。所以我的结论是,Eclipselink的字节码编织器生成了对getter的调用。
建立变通方法很容易,但我的问题是:为什么Eclipselink会这样做?
P.S:我在GlassFish Server开源版3.1.2(版本23)中使用EclipseLink 2.3.2.v20111125-r10461
答案 0 :(得分:0)
当启用编织时(Glassfish上默认),EclipseLink会将代码编织到属性get / set方法中,
对于更改跟踪支持,将编译set方法以检查新值是否与旧值不同,因此必须调用get方法以获取旧值。
现在这仍然很奇怪,因为你正在构建一个新对象,我不希望设置更改侦听器,所以希望绕过更改跟踪检查。您可以反编译代码以确切了解生成的内容。
最简单的解决方法是在get方法中进行空检查,这对于您的代码来说可能是最好的。您还可以切换到字段访问,这不会产生get / set方法中的副作用问题。您也可以使用Converter来处理转换,而不是使用get / set方法进行转换。