|
Consider the following JPA classes:
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@Entity
@Inheritance
public abstract class Card {}
@Entity @DiscriminatorValue("ability") public class AbilityCard extends Card {}
@Entity @DiscriminatorValue("leader") public class LeaderCard extends AbilityCard {}
@Entity @DiscriminatorValue("unit") public class UnitCard extends Card {}
@Entity @DiscriminatorValue("hero") public class HeroCard extends UnitCard {}
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@Entity
@Inheritance
public abstract class CardInstance<C extends Card> {
@ManyToOne(fetch = FetchType.EAGER)
private C card;
}
@Entity @DiscriminatorValue("ability") public class AbilityCardInstance extends CardInstance<AbilityCard> {}
@Entity @DiscriminatorValue("leader") public class LeaderCardInstance extends CardInstance<LeaderCard> {}
@Entity @DiscriminatorValue("unit") public class UnitCardInstance extends CardInstance<UnitCard> {}
@Entity @DiscriminatorValue("hero") public class HeroCardInstance extends CardInstance<HeroCard> {}
If I assign a LeaderCardInstance and a HeroCardInstance to a player named john, the database tables contain the following values:
|
PLAYER
|
CARD
|
CARD INSTANCE
|
| john
|
leader
|
leader
|
| john
|
hero
|
hero
|
However, if I now fetch john from the database, I find that all cards are of type HeroCard instead of their actual types. This is what I get:
|
PLAYER
|
CARD
|
CARD INSTANCE
|
| john
|
hero
|
leader
|
| john
|
hero
|
hero
|
Note how a HeroCard has been assigned to a LeaderCardInstance.
After reading the JPA spec, I made the following change and everything started working as expected:
@ManyToOne(fetch = FetchType.EAGER, targetEntity = Card.class)
private C card;
I have also examined the generated SQL with and without targetEntity and found that in the former case, the discriminator column of Card is fetched and thus the correct Card instance is populated whereas in the later case the discriminator column is not fetched.
This behaviour is seen with every 4.3.x version. However, 4.2.x versions throw the following runtime exception:
org.hibernate.WrongClassException: Object with id: 2 was not of the specified subclass: org.example.model.AbilityCard (Discriminator: hero)
My question is why does Hibernate pick one specific subclass of Card on the 4.3.x branch? Is this a bug?
I have attached a Gradle project. Running gradle clean test -i will display a table that demonstrates the problem.
This code is also available on Github.
|