hibernate/hibernate-orm · error · MappingException
No discriminator defined by '{}' which is a root class in a
Error message
No discriminator defined by '{}' which is a root class in a 'SINGLE_TABLE' inheritance hierarchy What it means
SingleTableSubclass.validate() enforces that the root of a SINGLE_TABLE inheritance hierarchy declares a discriminator, because all sibling classes share one table and rows can only be distinguished by the discriminator column/value. If the root PersistentClass has no discriminator after mapping processing, this MappingException names the offending root entity.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java:40
* @author Gavin King
*/
public final class SingleTableSubclass extends Subclass {
public SingleTableSubclass(PersistentClass superclass, MetadataBuildingContext buildingContext) {
super( superclass, buildingContext );
}
protected List<Property> getNonDuplicatedProperties() {
return new JoinedList<>( getSuperclass().getUnjoinedProperties(), getUnjoinedProperties() );
}
public Object accept(PersistentClassVisitor mv) {
return mv.accept( this );
}
public void validate(Metadata mapping) throws MappingException {
if ( getDiscriminator() == null ) {
throw new MappingException( "No discriminator defined by '" + getSuperclass().getEntityName()
+ "' which is a root class in a 'SINGLE_TABLE' inheritance hierarchy"
);
}
super.validate( mapping );
}
@Override
public void createConstraints(MetadataBuildingContext context) {
if ( !isAbstract() ) {
final var dialect = context.getMetadataCollector().getDatabase().getDialect();
if ( dialect.supportsTableCheck() ) {
final var discriminator = getDiscriminator();
final var selectables = discriminator.getSelectables();
if ( selectables.size() == 1 ) {
final var check = new StringBuilder();
check.append( selectables.get( 0 ).getText( dialect ) );
if ( isDiscriminatorValueNull() ) {
check.append( " is not " );View on GitHub (pinned to fad1729dce)
Solutions
- Add a discriminator to the root: @DiscriminatorColumn(name = "dtype", discriminatorType = StringType.INSTANCE) on the root entity, or <discriminator column="dtype" type="string"/> in hbm.xml.
- Give each class in the hierarchy an explicit @DiscriminatorValue (or discriminator-value attribute) so Hibernate never has to guess.
- If no discriminator is desired, switch the hierarchy to JOINED or TABLE_PER_CLASS inheritance, which do not need one.
- Validate the metadata at build time (new MetadataImpl(...).buildMetadata().validate()) in a CI test so a missing discriminator fails the build.
Example fix
<!-- before -->
<class name="Payment" table="payment">
<id name="id"/>
</class>
<subclass name="CreditCardPayment" extends="Payment"/>
<!-- after -->
<class name="Payment" table="payment">
<id name="id"/>
<discriminator column="dtype" type="string"/>
</class>
<subclass name="CreditCardPayment" extends="Payment" discriminator-value="CC"/> Defensive patterns
Strategy: validation
Validate before calling
// fail fast: validate inheritance mappings at boot Metadata md = sources.getMetadataBuilder().build(); md.buildMetadata().validate(); // throws MappingException naming the root entity missing a discriminator
Try / catch
try {
sf = cfg.buildSessionFactory();
} catch (MappingException e) {
if (e.getMessage().startsWith("No discriminator defined by")) {
String root = e.getMessage().split("'")[1]; // offending root entity name
log.error("Add @DiscriminatorColumn/<discriminator> to root entity {}", root);
}
throw e;
} Prevention
- Always declare @DiscriminatorColumn plus @DiscriminatorValue on every class in SINGLE_TABLE hierarchies.
- Write a metadata-validation test that calls MetadataImpl.validate() in CI.
- When removing discriminators, switch inheritance type to JOINED.
When it happens
Trigger: hbm.xml declaring <class> plus <subclass> (joined="false") with no <discriminator column="..."/> element on the root; JPA @Inheritance(SINGLE_TABLE) combined with mappings/annotations that suppress discriminator creation; explicit @DiscriminatorColumn removed while concrete subclasses exist.
Common situations: Hand-written hbm.xml that forgets the <discriminator> element; migrating from annotations to XML or vice versa and dropping @DiscriminatorColumn; mixing <subclass> with a root that relies on implicit discriminator creation that a setting (e.g. ignore-explicit-for-joined / implicit behavior changes) no longer provides after a Hibernate upgrade.
Related errors
- Root entity '<entityName>' is annotated '@DiscriminatorOptio
- Class '<className>' is not the root class of an entity inher
- Class '<componentClassName>' is an '@Embeddable' type and ma
- Mapped superclass '{}' may not specify an '@Inheritance' map
- Entity '{}' may not override the inheritance mapping strateg
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/0a015c3ac51a8cec.
Report an issue: GitHub.