{"record":{"id":"0a015c3ac51a8cec","repo":"hibernate/hibernate-orm","slug":"no-discriminator-defined-by-which-is-a-root-c","errorCode":null,"errorMessage":"No discriminator defined by '{}' which is a root class in a 'SINGLE_TABLE' inheritance hierarchy","messagePattern":"No discriminator defined by '(.+?)' which is a root class in a 'SINGLE_TABLE' inheritance hierarchy","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java","lineNumber":40,"sourceCode":" * @author Gavin King\n */\npublic final class SingleTableSubclass extends Subclass {\n\n\tpublic SingleTableSubclass(PersistentClass superclass, MetadataBuildingContext buildingContext) {\n\t\tsuper( superclass, buildingContext );\n\t}\n\n\tprotected List<Property> getNonDuplicatedProperties() {\n\t\treturn new JoinedList<>( getSuperclass().getUnjoinedProperties(), getUnjoinedProperties() );\n\t}\n\n\tpublic Object accept(PersistentClassVisitor mv) {\n\t\treturn mv.accept( this );\n\t}\n\n\tpublic void validate(Metadata mapping) throws MappingException {\n\t\tif ( getDiscriminator() == null ) {\n\t\t\tthrow new MappingException( \"No discriminator defined by '\" + getSuperclass().getEntityName()\n\t\t\t\t\t+ \"' which is a root class in a 'SINGLE_TABLE' inheritance hierarchy\"\n\t\t\t);\n\t\t}\n\t\tsuper.validate( mapping );\n\t}\n\n\t@Override\n\tpublic void createConstraints(MetadataBuildingContext context) {\n\t\tif ( !isAbstract() ) {\n\t\t\tfinal var dialect = context.getMetadataCollector().getDatabase().getDialect();\n\t\t\tif ( dialect.supportsTableCheck() ) {\n\t\t\t\tfinal var discriminator = getDiscriminator();\n\t\t\t\tfinal var selectables = discriminator.getSelectables();\n\t\t\t\tif ( selectables.size() == 1 ) {\n\t\t\t\t\tfinal var check = new StringBuilder();\n\t\t\t\t\tcheck.append( selectables.get( 0 ).getText( dialect ) );\n\t\t\t\t\tif ( isDiscriminatorValueNull() ) {\n\t\t\t\t\t\tcheck.append( \" is not \" );","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java#L22-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"<!-- before -->\n<class name=\"Payment\" table=\"payment\">\n    <id name=\"id\"/>\n</class>\n<subclass name=\"CreditCardPayment\" extends=\"Payment\"/>\n\n<!-- after -->\n<class name=\"Payment\" table=\"payment\">\n    <id name=\"id\"/>\n    <discriminator column=\"dtype\" type=\"string\"/>\n</class>\n<subclass name=\"CreditCardPayment\" extends=\"Payment\" discriminator-value=\"CC\"/>","handlingStrategy":"validation","validationCode":"// fail fast: validate inheritance mappings at boot\nMetadata md = sources.getMetadataBuilder().build();\nmd.buildMetadata().validate(); // throws MappingException naming the root entity missing a discriminator","typeGuard":null,"tryCatchPattern":"try {\n    sf = cfg.buildSessionFactory();\n} catch (MappingException e) {\n    if (e.getMessage().startsWith(\"No discriminator defined by\")) {\n        String root = e.getMessage().split(\"'\")[1]; // offending root entity name\n        log.error(\"Add @DiscriminatorColumn/<discriminator> to root entity {}\", root);\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["hibernate","inheritance","single-table","discriminator","orm-mapping"],"backgroundTag":"inheritance-mapping-error","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}