Inherited Embedded IDs

Posted on October 17, 2008 by Brian Tajuddin

I recently ran into a difficult problem using Hibernate through JPA. I had two tables that were effectively the same but had to be separate for various database optimization reasons. The two tables had equivalent primary key definitions. That definition was a composite key based off of two foreign keys. Here is the general view of what I had.

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Entity {
private IdClass _id;

@EmbeddedId
public IdClass getId() { return _id; }
protected void setId(IdClass id) { _id = id; }
}

@Entity
@Table(name="table_a")
public class EntityA extends Entity {}

@Entity
@Table(name="table_b")
public class EntityB extends Entity {}

@Embeddable
public class IdClass {
// a couple foreign keys
}

This caused some problems. There is a point in normal execution where we have to actually copy the values from one table to another. It turns out that the IDs are actually bound to a table as well as long as they are attached to the context. This made things very messy. I decided that it would be better for each of them to have their own IDs anyhow. I thought it would be easy to use inheritance on this. It was somewhat easy in the end, but there were definitely issues. Here is how I finally got everything working.

@MappedSuperclass
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Entity<T extends IdClass> {
private T _id;

@Transient
public T getId() { return _id; }
protected void setId(T id) { _id = id; }
}

@Entity
@Table(name="table_a")
public class EntityA extends Entity<IdClassA> {
@EmbeddedId
public IdClassA getId() { return super.getId(); }
}

@Entity
@Table(name="table_b")
public class EntityB extends Entity
<IdClassB> {
@EmbeddedId
public IdClassB getId() { return super.getId(); }
}

@MappedSuperclass
@Embeddable
public abstract class IdClass {
// a couple foreign keys
}

public class IdClassA extends IdClass {}

public class IdClassB extends IdClass{}

The secret sauce here is the getId() method in the two entity child classes. I tried several things before finding this. Without this, you will likely be getting ClassCastExceptions every time you try to retrieve one of these things. It would appear that it is retreiving something that is of the parent type but not the child. I don't really understand what is going on there. The other error I got (and can't remember the exact flavor of incantation to get it) involved a message saying that IdClassA had no id property.

All in all, it isn't a terribly complex solution, but it took a while to find the right combination of annotations and the magic getId() method in the subclasses. That was the hardest part to find.