hibernate/hibernate-orm · error · MappingException
discriminator mapping required for single table polymorphic
Error message
discriminator mapping required for single table polymorphic persistence
What it means
SingleTableEntityPersister requires every polymorphic SINGLE_TABLE mapping to have a discriminator, because all subclasses share one physical table and rows can only be told apart by the discriminator value. At SessionFactory boot, when persistentClass.isPolymorphic() is true but getDiscriminator() returns null, a MappingException is thrown. This is a mapping configuration error, not a runtime data problem.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java:211
final var columns = join.getKey().getColumns();
for ( int i = 0; i < columns.size(); i++ ) {
keyCols[i] = columns.get( i ).getQuotedName( dialect );
}
joinKeyColumns.add( keyCols );
}
subclassTableNameClosure = toStringArray( subclassTables );
subclassTableKeyColumnClosure = to2DStringArray( joinKeyColumns );
isClassOrSuperclassTable = toBooleanArray( isConcretes );
isClassOrSuperclassJoin = toBooleanArray( isClassOrSuperclassJoins );
isNullableSubclassTable = toBooleanArray( isNullables );
// DISCRIMINATOR
if ( persistentClass.isPolymorphic() ) {
final var discriminator = persistentClass.getDiscriminator();
if ( discriminator == null ) {
throw new MappingException( "discriminator mapping required for single table polymorphic persistence" );
}
forceDiscriminator = persistentClass.isForceDiscriminator();
final var selectable = discriminator.getSelectables().get( 0 );
discriminatorType = DiscriminatorHelper.getDiscriminatorType( persistentClass );
discriminatorValue = DiscriminatorHelper.getDiscriminatorValue( persistentClass );
discriminatorSQLValue = DiscriminatorHelper.getDiscriminatorSQLValue( persistentClass, dialect );
discriminatorInsertable = isDiscriminatorInsertable( persistentClass );
if ( selectable instanceof Formula formula ) {
discriminatorFormulaTemplate = formula.getTemplate( dialect, typeConfiguration );
discriminatorColumnName = null;
discriminatorColumnReaders = null;
// discriminatorColumnReaderTemplate = null;
discriminatorAlias = "clazz_";
}
else if ( selectable instanceof Column column ) {
discriminatorColumnName = column.getQuotedName( dialect );
discriminatorColumnReaders = column.getReadExpr( dialect );
// discriminatorColumnReaderTemplate = column.getTemplate( dialect, typeConfiguration );View on GitHub (pinned to fad1729dce)
Solutions
- Add a discriminator to the root: @DiscriminatorColumn (JPA) or <discriminator column="dtype"/> (hbm.xml)
- Give each class in the hierarchy an explicit @DiscriminatorValue / discriminator-value
- If sharing one table is not required, switch the hierarchy to JOINED or TABLE_PER_CLASS
- When building mappings programmatically, define the discriminator before subclasses are processed
Example fix
// before: polymorphic single-table root, no discriminator
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class BillingDetails { ... }
@Entity
public class CreditCard extends BillingDetails { ... }
// after (hbm equivalent: <discriminator column="dtype"/>)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype", discriminatorType = DiscriminatorType.STRING)
public class BillingDetails { ... }
@Entity
@DiscriminatorValue("CC")
public class CreditCard extends BillingDetails { ... } Defensive patterns
Strategy: validation
Validate before calling
// after Metadata build, before SessionFactory
for (PersistentClass pc : metadata.getEntityBindings()) {
if (pc instanceof RootClass root && root.isPolymorphic() && root.getDiscriminator() == null) {
throw new IllegalStateException("Polymorphic root without discriminator: " + root.getEntityName());
}
} Try / catch
try { sessionFactory = cfg.buildSessionFactory(); } catch (MappingException e) { if (e.getMessage().contains("discriminator mapping required")) { /* add @DiscriminatorColumn / <discriminator> to the root */ } throw e; } Prevention
- Always declare @DiscriminatorColumn on SINGLE_TABLE hierarchy roots
- Include a SessionFactory build test in CI for every persistence unit
- When migrating hbm.xml, check that each polymorphic root still has a <discriminator> element
When it happens
Trigger: hbm.xml <class> with <subclass> elements but no <discriminator> element; programmatic mapping that adds subclasses to a root without defining a discriminator; mappings where discriminator configuration was lost during generation or migration.
Common situations: Legacy hbm.xml mappings brought forward from very old Hibernate versions; tooling that generates mappings without discriminators; mixing JPA annotations and hbm fragments for the same hierarchy.
Related errors
- Expecting just a single formula/column in context of <%s nam
- Illegal discriminator type: {discriminatorType}
- Could not parse discriminator value '{discriminatorValue}' a
- Discriminator formulas on joined inheritance hierarchies not
- Encountered 'subclass table index' [%s] was outside expected
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/7cfa5f9c01f7e0d3.
Report an issue: GitHub.