基本问题:为什么@Embedded对象并不总是实例化?
有趣的观察结果是,如果对象不包含基本数据类型(int,boolean...)或以前没有接触过,则Ebean不会实例化@Embedded对象。例:
@Entity
public class Embedder {
// getNotAutoInstantiated() will return null if this field was not touched before
@Embedded
private NotAutoInstantiated notAutoInstantiated = new NotAutoInstantiated();
// getAutoInstantiated() will always return an instance!
@Embedded
private AutoInstantiated autoInstantiated = new AutoInstantiated();
}
@Embeddable
public class AutoInstantiated {
// theKey is why this embedded object is always instantiated
private int theKey;
private String field1;
}
@Embeddable
public class NotAutoInstantiated {
private String field2;
}
对于休眠,您可能需要查看问题 HHH-7610。
特别是,从 5.1 开始,有一个实验性功能可以更改此行为。 请注意,此功能存在已知问题,在稳定之前不应在生产中使用。 这在 Javadocs for org.hibernate.cfg.AvailableSettings 中有详细说明):
/**
* [EXPERIMENTAL] Enable instantiation of composite/embedded objects when all of its attribute values are {@code null}.
* The default (and historical) behavior is that a {@code null} reference will be used to represent the
* composite when all of its attributes are {@code null}
* <p/>
* This is an experimental feature that has known issues. It should not be used in production
* until it is stabilized. See Hibernate Jira issue HHH-11936 for details.
*
* @since 5.1
*/
String CREATE_EMPTY_COMPOSITES_ENABLED = "hibernate.create_empty_composites.enabled";
将hibernate.create_empty_composites.enabled
属性设置为 true,瞧!
我不认为 JPA 规范清楚地描述了当@Embedded
对象的属性都是空时会发生什么,但至少有一些实现将具有空属性的对象视为空对象,这就是你所看到的。
这似乎是一个合理的实现。当然,它在我的代码(使用 Hibernate)中很有用,如果我将 @Embedded
对象设置为 null,我希望它在加载持久化版本时保持 null。
在您的示例中,AutoInstantiated
类永远不能被视为 null,因为基元属性 theKey
永远不能为 null。
我刚刚在Hibernate上遇到了同样的问题。最初的问题,关于"为什么"得到了回答。
但是要谈论解决方案,我只使用@PostLoad方法,因此在类中嵌入器如下所示:
@PostLoad
private void initData() {
if(notAutoInstantiated == null) {
notAutoInstantiated = new NotAutoInstantiated();
}
}
更新:
警告!上面的代码正在工作,但确实有一个意想不到的副作用!一旦你从数据库中加载了带有空指针的对象,它就会被标记为脏,因为这个后加载代码!就我而言,这种副作用会导致线程发出 SQL 更新命令,该命令应该只加载搜索此错误的数据和小时数!
与 Martin 类似的技巧,用于在对象未加载时避免 NPE 并避免副作用。
// workaround as Embeddable objects are not loaded if all their fields are null
public NotAutoInstantiated getNotAutoInstantiated() {
if (notAutoInstantiated == null) {
// WARNING! do not assign this new object to notAutoInstantiated field,
// this would have side effects as the field is not loaded by Ebean,
// Ebean would try to refresh the field in some cases and another NPE would occur:
// io.ebeaninternal.server.core.DefaultBeanLoader.refreshBeanInternal(DefaultBeanLoader.java:194)
return new NotAutoInstantiated();
}
return notAutoInstantiated;
}