Ben
Ben

Reputation: 1982

JPA inheritance using SINGLE_TABLE - discriminator value doesn't match class

I've got a strange behaviour with a JPA class hierarchy using a single table. Basically I have two entities EntityMapA and EntityMapB which both extend EntityMap. The discriminator value is 'ENTITY_TYPE' and it is A for EntityMapA and B for EntityMapB. Somehow I get Objects of type EntityMapA where the discriminator value is set to 'B'!

I am using Hibernate 3.3 as the JPA provider.

Here is the code:

@Entity
@Table(name="ENTITY_MAP")
@DiscriminatorColumn(name = "ENTITY_TYPE")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class EntityMap implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private Long entityMapId;

    //This is a ID of another entity we map to. It is a different entity type depending on 
    //The subclass. For EntityMapA it would map to EntityA and for EntityMapB it would map   
    // to EntityB
    private Long entityId;


    private String discriminator;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "ENTITY_MAP_ID", unique = true, nullable = false)
    public Long getEntityMapId() {
        return entityMapId;
    }
    public void setEntityMapId(Long EntityMapId) {
        this.entityMapId = entityMapId;
    }


    @Column(name="ENTITY_TYPE",insertable=false,updatable=false)
    public String getDiscriminator() {
        return discriminator;
    }
    public void setDiscriminator(String discriminator) {
        this.discriminator = discriminator;
    }

    @Column(name="ENTITY_ID",insertable=false,updatable=false)
    public Long getEntityId() {
        return entityId;
    }
    public void setEntityId(Long entityId) {
        this.entityId = entityId;
    }

    //there are other common fields in here which are left out    
}


@Entity
@DiscriminatorValue("A")
public class EntityMapA extends EntityMap {

    /**
     *
     */
    private static final long serialVersionUID = -8709307036005000705L;

    private EntityA entityA;

    public static final String DISCRIMINATOR = "A";

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ENTITY_ID", nullable = false)
    @NotNull
    public EntityA getEntityA() {
        return entityA;
    }

    public void setEntityA(EntityA entityA) {
        this.entityA = entityA;
    }

}



@Entity
@DiscriminatorValue("B")
public class EntityMapB extends EntityMap {

    /**
     *
     */
    private static final long serialVersionUID = -8709307036005000705L;

    public static final String DISCRIMINATOR = "B";

    private EntityB entityB;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ENTITY_ID", nullable = false)
    @NotNull
    public EntityB getEntityB() {
        return entityB;
    }

    public void setEntityB(EntityB entityB) {
        this.entityB = entityB;
    }
}

And then finally I have a mapping in EntityA:

    @OneToMany(cascade=CascadeType.ALL,fetch = FetchType.LAZY, mappedBy = "entityA")
    public List<EntityMapA> getEntityMaps() {
        return entityMaps;
    }

    public void setEntityMaps(List<EntityMapA> entityMaps) {
        this.entityMaps = entityMaps;
    }

Now I have a ENTITY_MAP row with ENTITY_TYPE = "B" and ENTITY_ID = "12345" There are both a EntityA with the id "12345" and a EntityB with the same ID "12345".

Now when I load EntityA with the id "12345" is has one entry in the getEntityMaps() of type EntityMapA but the discriminator value on that EntityMapA is 'B'.

What is going wrong here? Why is a row with the discriminator 'B' mapped to a EntityMapA entity?

Is it because I map the EntityId twice (once with @Column(name="ENTITY_TYPE",insertable=false,updatable=false) on the parent class and then again per JoinColumn to the actual entity?

UPDATE Btw, the EntityMapA listed in the collection on EntityA is never really created. When you try to load the entity you get an exception telling you that that entity doesn't exists. So looks like a bug in Hibernate to me.

Upvotes: 1

Views: 7981

Answers (1)

JB Nizet
JB Nizet

Reputation: 692131

The problem is that you use the same column to map two different associations. AFAIK, this is not supported.

And it's actually a good thing, because it's much cleaner to use two separate columns to refer to different things. It allows defining an FK constraint on those columns, for example, which isn't possible with your current solution since the column holds IDs of EntityA or EntityB depending on the row.

Upvotes: 2

Related Questions