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

  1. 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.
  2. Give each class in the hierarchy an explicit @DiscriminatorValue (or discriminator-value attribute) so Hibernate never has to guess.
  3. If no discriminator is desired, switch the hierarchy to JOINED or TABLE_PER_CLASS inheritance, which do not need one.
  4. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/0a015c3ac51a8cec. Report an issue: GitHub.