Java @如果嵌入式对象没有基本数据类型字段,则不会自动实例化

Java @如果嵌入式对象没有基本数据类型字段,则不会自动实例化,java,jpa,ebean,embeddable,Java,Jpa,Ebean,Embeddable,基本问题:为什么@Embedded对象不总是实例化 有趣的观察结果是,如果@Embedded对象不包含基本数据类型(int、boolean…),或者之前没有涉及过,那么Ebean不会实例化这些对象。例如: @Entity public class Embedder { // getNotAutoInstantiated() will return null if this field was not touched before @Embedded private Not

基本问题:为什么@Embedded对象不总是实例化

有趣的观察结果是,如果@Embedded对象不包含基本数据类型(int、boolean…),或者之前没有涉及过,那么Ebean不会实例化这些对象。例如:

@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;      
}

我不认为JPA规范清楚地描述了当
@Embedded
对象的属性都为null时应该发生什么,但至少有些实现将具有null属性的对象视为null对象,这就是您所看到的

这似乎是一个合理的实现。当然,它在我的代码(使用Hibernate)中很有用,如果我将
@Embedded
对象设置为null,我希望在加载持久化版本时它保持为null


在您的示例中,
自动实例化的
类永远不能被视为null,因为原语属性
永远不能为null。

对于Hibernate,您可能需要检查问题

特别是,由于5.1,有一个实验特性可以改变这种行为。请注意,此功能存在已知问题,在稳定之前不应在生产中使用。这在Javadocs中有详细说明:

/**
*[实验性]当复合/嵌入对象的所有属性值为{@code null}时,启用该对象的实例化。
*默认(和历史)行为是{@code null}引用将用于表示
*当其所有属性都为{@code null}时的复合
*

*这是一个具有已知问题的实验特性。它不应该用于生产 *直到它稳定下来。有关详细信息,请参阅Hibernate Jira发行版HHH-11936。 * *@自5.1 */ 字符串CREATE\u EMPTY\u COMPOSITES\u ENABLED=“hibernate.CREATE\u EMPTY\u COMPOSITES.ENABLED”;


hibernate.create_empty_composites.enabled
属性设置为true,然后瞧

我刚刚在Hibernate上遇到了同样的问题。 关于“为什么”的原始问题得到了回答

但为了讨论解决方案,我只使用@PostLoad方法,因此在类嵌入器中类似于:

@PostLoad
private void initData() {
  if(notAutoInstantiated == null) {
    notAutoInstantiated = new NotAutoInstantiated();
  }
}
更新:

警告上面的代码正在工作,但确实有意外的副作用!一旦从数据库加载带有空指针的对象,它就会被标记为脏对象,因为这是加载后代码!
在我的例子中,这个副作用会导致一个线程发出一个SQL更新命令,该命令应该只加载数据和搜索这个bug的时间

一种类似于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;
  }

多个嵌入对象之间的字段名冲突可能存在问题。通常,JPA提供程序将嵌入式对象字段映射到父表中的列。如果嵌入的字段名运行时间过长,则可能会导致数据库中的字段名被截断,从而导致列名发生冲突。该示例可能具有欺骗性。在我的代码中,名称要短得多,而且它们的前缀也不相同。但即使在示例中,字段也以“not”和“auto”开头,因此截断也不是一个大问题。实际上,我在考虑“field”和“field”。哦,谢谢。是的,Ebean不允许这样做,并在启动数据库之前抛出异常。错误仅在示例中。不幸的是,情况比这更糟。埃宾似乎与这种行为不一致。我们有一个非常复杂的数据模型,其中像@Embedded fields这样的自动实例化在大多数地方都是自动实例化的。但有一个地方不会发生这种情况。我们没能发现那件事的特点。“保留null”方法对我来说不是最好的,因为它需要另一个null检查。这是Java中最糟糕的做法。在这种情况下很容易避免……谢谢,很高兴知道这一点。然而,这个问题与埃宾有关。Hibernate可能在很多方面都很优秀,但Ebean更适合小型项目。
  // 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;
  }