shelley shelley - 3 months ago 31x
Java Question

Hibernate WrongClassException for Custom Discriminators

I have a concrete JPA entity superclass mapped with the

using discriminator columns, and have a couple subclasses entities that extend this superclass with additional properties.

@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "TYPE")
public class BaseEntity {
// . . .

public class SubclassEntity extends BaseEntity {
// . . .

There are cases where I want to specify additional discriminator values without having to explicitly define a subclass for every type (that is, not every "BaseEntity" specifies additional properties that warrant a subclass / separate table). This strategy works fine in the database design as well as the Java class hierarchy, however, Hibernate JPA does not allow this and throws a
because there isn't a subclass mapped to the discriminator:

Caused by: org.hibernate.WrongClassException: Object [id=entity-1] was not of the specified subclass [] : Discriminator: custom-1
at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.getConcreteEntityTypeName(
at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.hydrateEntityState(
at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.readRow(
at org.hibernate.loader.plan.exec.internal.EntityLoadQueryDetails$EntityLoaderRowReader.readRow(
at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(
at org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader.load(
at org.hibernate.persister.entity.AbstractEntityPersister.load(
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(
at org.hibernate.event.internal.DefaultLoadEventListener.load(
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(
at org.hibernate.internal.SessionImpl.fireLoad(
at org.hibernate.internal.SessionImpl.internalLoad(
at org.hibernate.type.EntityType.resolveIdentifier(
at org.hibernate.type.EntityType.resolve(
at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(
at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(
at org.hibernate.loader.Loader.processResultSet(
at org.hibernate.loader.Loader.doQuery(
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(
at org.hibernate.loader.Loader.doList(
at org.hibernate.loader.Loader.doList(
at org.hibernate.loader.Loader.listIgnoreQueryCache(
at org.hibernate.loader.Loader.list(
at org.hibernate.loader.hql.QueryLoader.list(
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(
at org.hibernate.internal.SessionImpl.list(
at org.hibernate.internal.QueryImpl.list(
at org.hibernate.jpa.internal.QueryImpl.list(
at org.hibernate.jpa.internal.QueryImpl.getResultList(

In this case, I want Hibernate to return the concrete base entity
rather than trying to instantiate a subclass. I don't see anything in the JPA spec (JSR 338) that indicates this shouldn't be possible (although the spec doesn't explicitly call out this scenario either).

Is there any way to allow JPA/Hibernate to allow custom discriminator types without requiring subclasses?


Unfortunately Hibernate expects exactly one discriminator value per entity type. And I guess that there is no difference to other JPA providers, as you can't define more than one DiscriminatorValue for an entity class.

Even if you define no DiscriminatorValue, there will be exactly one:

If the DiscriminatorValue annotation is not specified and a discriminator column is used, a provider-specific function will be used to generate a value representing the entity type. If the DiscriminatorType is STRING, the discriminator value default is the entity name.

(excerpt from the JavaDoc of DiscriminatorValue)

But you can define a DiscriminatorFormula instead of a DiscriminatorColumn in Hibernate:

@Inheritance(strategy = InheritanceType.JOINED)
public class BaseEntity {
    // . . .

public class SubclassEntity extends BaseEntity {
    // ...

Disadvantage of that solution: You need to declare the discriminator values of all subtypes in BaseEntity.