{"record":{"id":"7cfa5f9c01f7e0d3","repo":"hibernate/hibernate-orm","slug":"discriminator-mapping-required-for-single-table-po","errorCode":null,"errorMessage":"discriminator mapping required for single table polymorphic persistence","messagePattern":"discriminator mapping required for single table polymorphic persistence","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java","lineNumber":211,"sourceCode":"\t\t\tfinal var columns = join.getKey().getColumns();\n\t\t\tfor ( int i = 0; i < columns.size(); i++ ) {\n\t\t\t\tkeyCols[i] = columns.get( i ).getQuotedName( dialect );\n\t\t\t}\n\t\t\tjoinKeyColumns.add( keyCols );\n\t\t}\n\n\t\tsubclassTableNameClosure = toStringArray( subclassTables );\n\t\tsubclassTableKeyColumnClosure = to2DStringArray( joinKeyColumns );\n\t\tisClassOrSuperclassTable = toBooleanArray( isConcretes );\n\t\tisClassOrSuperclassJoin = toBooleanArray( isClassOrSuperclassJoins );\n\t\tisNullableSubclassTable = toBooleanArray( isNullables );\n\n\t\t// DISCRIMINATOR\n\n\t\tif ( persistentClass.isPolymorphic() ) {\n\t\t\tfinal var discriminator = persistentClass.getDiscriminator();\n\t\t\tif ( discriminator == null ) {\n\t\t\t\tthrow new MappingException( \"discriminator mapping required for single table polymorphic persistence\" );\n\t\t\t}\n\t\t\tforceDiscriminator = persistentClass.isForceDiscriminator();\n\t\t\tfinal var selectable = discriminator.getSelectables().get( 0 );\n\t\t\tdiscriminatorType = DiscriminatorHelper.getDiscriminatorType( persistentClass );\n\t\t\tdiscriminatorValue = DiscriminatorHelper.getDiscriminatorValue( persistentClass );\n\t\t\tdiscriminatorSQLValue = DiscriminatorHelper.getDiscriminatorSQLValue( persistentClass, dialect );\n\t\t\tdiscriminatorInsertable = isDiscriminatorInsertable( persistentClass );\n\t\t\tif ( selectable instanceof Formula formula ) {\n\t\t\t\tdiscriminatorFormulaTemplate = formula.getTemplate( dialect, typeConfiguration );\n\t\t\t\tdiscriminatorColumnName = null;\n\t\t\t\tdiscriminatorColumnReaders = null;\n//\t\t\t\tdiscriminatorColumnReaderTemplate = null;\n\t\t\t\tdiscriminatorAlias = \"clazz_\";\n\t\t\t}\n\t\t\telse if ( selectable instanceof Column column ) {\n\t\t\t\tdiscriminatorColumnName = column.getQuotedName( dialect );\n\t\t\t\tdiscriminatorColumnReaders = column.getReadExpr( dialect );\n//\t\t\t\tdiscriminatorColumnReaderTemplate = column.getTemplate( dialect, typeConfiguration );","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java#L193-L229","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: polymorphic single-table root, no discriminator\n@Entity\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\npublic class BillingDetails { ... }\n\n@Entity\npublic class CreditCard extends BillingDetails { ... }\n\n// after (hbm equivalent: <discriminator column=\"dtype\"/>)\n@Entity\n@Inheritance(strategy = InheritanceType.SINGLE_TABLE)\n@DiscriminatorColumn(name = \"dtype\", discriminatorType = DiscriminatorType.STRING)\npublic class BillingDetails { ... }\n\n@Entity\n@DiscriminatorValue(\"CC\")\npublic class CreditCard extends BillingDetails { ... }","handlingStrategy":"validation","validationCode":"// after Metadata build, before SessionFactory\nfor (PersistentClass pc : metadata.getEntityBindings()) {\n    if (pc instanceof RootClass root && root.isPolymorphic() && root.getDiscriminator() == null) {\n        throw new IllegalStateException(\"Polymorphic root without discriminator: \" + root.getEntityName());\n    }\n}","typeGuard":null,"tryCatchPattern":"try { sessionFactory = cfg.buildSessionFactory(); } catch (MappingException e) { if (e.getMessage().contains(\"discriminator mapping required\")) { /* add @DiscriminatorColumn / <discriminator> to the root */ } throw e; }","preventionTips":["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"],"tags":["hibernate","orm","single-table-inheritance","discriminator","mapping","session-factory-boot"],"backgroundTag":"missing-discriminator-column","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}