{"record":{"id":"ccebe8aac19ee336","repo":"hibernate/hibernate-orm","slug":"secondary-table-explicittablename-for-propert","errorCode":null,"errorMessage":"Secondary table '${explicitTableName}' for property '${propertyName}' of entity'${className}' is not declared (use '@SecondaryTable' to declare the secondary table)","messagePattern":"Secondary table '(.+?)' for property '(.+?)' of entity'(.+?)' is not declared \\(use '@SecondaryTable' to declare the secondary table\\)","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumns.java","lineNumber":89,"sourceCode":"\tpublic void setBuildingContext(MetadataBuildingContext buildingContext) {\n\t\tthis.buildingContext = buildingContext;\n\t}\n\n\tpublic MetadataBuildingContext getBuildingContext() {\n\t\treturn buildingContext;\n\t}\n\n\tpublic void setJoins(Map<String, Join> joins) {\n\t\tthis.joins = joins;\n\t}\n\n\tpublic Join getJoin() {\n\t\tfinal var firstColumn = columns.get( 0 );\n\t\tfinal String explicitTableName = firstColumn.getExplicitTableName();\n\t\t//note: checkPropertyConsistency() is responsible for ensuring they all have the same table name\n\t\tfinal var join = getJoin( explicitTableName );\n\t\tif ( join == null ) {\n\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\"Secondary table '\" + explicitTableName + \"' for property '\" + propertyName + \"' of entity'\" + getPropertyHolder().getClassName()\n\t\t\t\t\t\t\t+ \"' is not declared (use '@SecondaryTable' to declare the secondary table)\"\n\t\t\t);\n\t\t}\n\t\telse {\n\t\t\treturn join;\n\t\t}\n\t}\n\n\tprivate Join getJoin(String explicitTableName) {\n\t\tfinal var join = joins.get( explicitTableName );\n\t\tif ( join != null ) {\n\t\t\treturn join;\n\t\t}\n\t\telse {\n\t\t\t// annotation binding seems to use logical and physical naming somewhat inconsistently...\n\t\t\tfinal String physicalTableName =\n\t\t\t\t\tgetBuildingContext().getMetadataCollector()","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumns.java#L71-L107","documentation":"When a column declares @Column(table = \"...\"), AnnotatedColumns.getJoin() resolves the secondary table by that explicit name from the joins map built from the entity's @SecondaryTable declarations. If no declared secondary table matches, binding throws this AnnotationException naming the table, the property, and the entity, and suggesting '@SecondaryTable'.","triggerScenarios":"@Column(table = \"X\") on a property of an entity where X is not declared via @SecondaryTable on that entity - a typo, different case, the declaration sitting on another entity in a @MappedSuperclass chain that does not propagate, or simply a forgotten declaration.","commonSituations":"Forgetting @SecondaryTable when moving columns to a side table; renaming a table in one place; copy-pasting entities with secondary-table columns; assuming @SecondaryTable from a superclass is inherited when it is not visible to this mapping.","solutions":["Add @SecondaryTable(name = \"X\", ...) to the entity with exactly the name used in @Column(table = \"X\")","Fix typos or letter-case differences between the two names (also check catalog/schema qualifiers)","Ensure the @SecondaryTable is declared on the same entity (or properly inherited mapped superclass) that owns the property","Verify pkJoinColumn/joinColumn setup once the table resolves, since that is checked next"],"exampleFix":"// before: secondary table used but never declared\n@Entity\npublic class User {\n    @Id private Long id;\n\n    @Column(table = \"user_audit\")        // not declared -> error\n    private String lastModifiedBy;\n}\n\n// after: declare the secondary table on the entity\n@Entity\n@SecondaryTable(name = \"user_audit\",\n        pkJoinColumns = @PrimaryKeyJoinColumn(name = \"user_id\"))\npublic class User {\n    @Id private Long id;\n\n    @Column(table = \"user_audit\")\n    private String lastModifiedBy;\n}","handlingStrategy":"validation","validationCode":"// Before boot: every @Column(table=...) name must be a declared @SecondaryTable name\nstatic boolean secondaryTableReferencesValid(Class<?> entity) {\n    Set<String> declared = new HashSet<>();\n    SecondaryTable st = entity.getAnnotation(SecondaryTable.class);\n    if (st != null) declared.add(st.name());\n    SecondaryTables sts = entity.getAnnotation(SecondaryTables.class);\n    if (sts != null) Arrays.stream(sts.value()).forEach(t -> declared.add(t.name()));\n    for (Field f : entity.getDeclaredFields()) {\n        Column c = f.getAnnotation(Column.class);\n        if (c != null && !c.table().isEmpty() && !declared.contains(c.table())) return false;\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n    sessionFactory = metadata.buildSessionFactory();\n} catch (AnnotationException e) {\n    if (e.getMessage().contains(\"is not declared (use '@SecondaryTable'\")) {\n        throw new IllegalStateException(\"@Column(table=...) references an undeclared secondary table\", e);\n    }\n    throw e;\n}","preventionTips":["Declare every secondary table on the entity before referencing it from @Column","Keep table names in constants to avoid drift between declaration and usage","When renaming a secondary table, search for all @Column(table = ...) references"],"tags":["hibernate","jpa","secondary-table","column","annotation","mapping"],"backgroundTag":"undeclared-secondary-table","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}