views:

45

answers:

1

I have a problem trying to map an inheritance tree. A simplified version of my model is like this:

@MappedSuperclass
@Embeddable
public class BaseEmbedded implements Serializable {

   @Column(name="BE_FIELD")
   private String beField;

   // Getters and setters follow
}

@MappedSuperclass
@Embeddable
public class DerivedEmbedded extends BaseEmbedded {

  @Column(name="DE_FIELD")
   private String deField;

   // Getters and setters follow
}

@MappedSuperclass
public abstract class BaseClass implements Serializable {

   @Embedded
   protected BaseEmbedded embedded;

    public BaseClass() {
      this.embedded = new BaseEmbedded();
    }

     // Getters and setters follow    
}

@Entity
@Table(name="MYTABLE")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TYPE", discriminatorType=DiscriminatorType.STRING)
public class DerivedClass extends BaseClass {

    @Id
    @Column(name="ID", nullable=false)
    private Long id;

    @Column(name="TYPE", nullable=false, insertable=false, updatable=false)
    private String type;

    public DerivedClass() {
        this.embedded = new DerivedClass();
    }

    // Getters and setters follow
}

@Entity
@DiscriminatorValue("A")
public class DerivedClassA extends DerivedClass {

    @Embeddable
    public static NestedClassA extends DerivedEmbedded {

            @Column(name="FIELD_CLASS_A")
            private String fieldClassA;
    }

    public DerivedClassA() {
        this.embedded = new NestedClassA();
    }

    // Getters and setters follow
}

@Entity
@DiscriminatorValue("B")
public class DerivedClassB extends DerivedClass {

    @Embeddable
    public static NestedClassB extends DerivedEmbedded {

            @Column(name="FIELD_CLASS_B")
            private String fieldClassB;
    }

    public DerivedClassB() {
        this.embedded = new NestedClassB();
    }

    // Getters and setters follow
}

At Java level, this model is working fine, and I believe is the appropriate one. My problem comes up when it's time to persist an object.

At runtime, I can create an object which could be an instance of DerivedClass, DerivedClassA or DerivedClassB. As you can see, each one of the derived classes introduces a new field which only makes sense for that specific derived class. All the classes share the same physical table in the database. If I persist an object of type DerivedClass, I expect fields BE_FIELD, DE_FIELD, ID and TYPE to be persisted with their values and the remaining fields to be null. If I persist an object of type DerivedClass A, I expect those same fields plus the FIELD_CLASS_A field to be persisted with their values and field FIELD_CLASS_B to be null. Something equivalent for an object of type DerivedClassB.

Since the @Embedded annotation is at the BaseClass only, Hibernate is only persisting the fields up to that level in the tree. I don't know how to tell Hibernate that I want to persist up to the appropriate level in the tree, depending on the actual type of the embedded property.

I cannot have another @Embedded property in the subclasses since this would duplicate data that is already present in the superclass and would also break the Java model.

I cannot declare the embedded property to be of a more specific type either, since it's only at runtime when the actual object is created and I don't have a single branch in the hierarchy.

Is it possible to solve my problem? Or should I resignate myself to accept that there is no way to persist the Java model as it is?

Any help will be greatly appreciated.

A: 

Wow. This is the simplified version? I assume that the behavior that you are seeing is that BaseEmbedded field is persisted but not the FIELD_CLASS_A or B?

The problem is that when Hibernate maps the DerivedClassA and B classes, it reflects and sees the embedded field as a BaseEmbedded class. Just because you then persist an object with the embedded field being a NestedClass, the mapping has already been done and the FIELD_CLASS_A and B are never referenced.

What you need to do is to get rid of the NestedClass* and embedded field and instead have the fieldClassA and B be normal members of DerivedClassA and B. Then add add a name field to the @Entity which will put them both in the same table I believe. This will allow you to collapse/simplify your class hierarchy a lot further.

See: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e1168

@Entity(name = "DerivedClass")
@DiscriminatorValue("A")
public class DerivedClassA extends DerivedClass {

    @Column(name="FIELD_CLASS_A")
    private String fieldClassA;

...

@Entity(name = "DerivedClass")
@DiscriminatorValue("B")
public class DerivedClassB extends DerivedClass {

    @Column(name="FIELD_CLASS_B")
    private String fieldClassB;

...
Gray