質問

Fundamental question: Why aren't @Embedded objects always instantiated?

The interesting observation is that Ebean does not instantiate @Embedded objects if those do not contain basic datatypes (int, boolean...) or weren't touched before. Example:

@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;      
}
役に立ちましたか?

解決 2

I don't think the JPA spec clearly describes what should happen when an @Embedded object's properties are all null, but at least some implementations treat an object with null properties as a null object, which is what you're seeing.

This seems like a reasonable implementation. Certainly it's been useful in my code (using Hibernate) where if I set an @Embedded object to null I want it to remain null when I load a persisted version.

In your example, the AutoInstantiated class can never be considered null, since the primitive property theKey can never be null.

他のヒント

For Hibernate, you might want to check out issue HHH-7610.

In particular, since 5.1 there is an experimental feature to change this behavior. Note that this feature has known issues, and should not be used in production until it is stabilized. This is detailed in the 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";

Set the hibernate.create_empty_composites.enabled property to true and voilà!

I just hit the same issue with Hibernate. The original question, about the "why" is answered.

But to talk about a solution, I just use a @PostLoad method, so within class Embedder something like:

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

Update:

Warning! The upper code is working, but does have an unexpected side effect! As soon as you load your object with null pointer from the database, it is marked as dirty, because of this post load code! In my case, this side effects leads to an SQL update command from a thread, which should only load the data and hours of searching this bug!

A similar hack as Martin to avoid NPE when the object is not loaded and avoid side effects.

  // 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;
  }
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top