hibernate/hibernate-orm · error · MappingException

Secondary table (<join/>) must explicitly name table or sub-

Error message

Secondary table (<join/>) must explicitly name table or sub-select, but neither specified for entity [%s]

What it means

An hbm <join> maps a secondary table and must identify it either by an explicit table name (table attribute) or by a sub-select (subselect attribute). Helper.createTableSource returns a TableSource for named tables; when the explicit table name comes back empty - neither table= nor subselect= was supplied - bootstrap fails naming the entity whose <join> is incomplete.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/SecondaryTableSourceImpl.java:50

	private final String logicalTableName;
	private final List<ColumnSource> keyColumnSources;

	public SecondaryTableSourceImpl(
			MappingDocument sourceMappingDocument,
			final JaxbHbmSecondaryTableType jaxbSecondaryTableMapping,
			EntityNamingSource entityNamingSource,
			Helper.InLineViewNameInferrer inLineViewNameInferrer) {
		super( sourceMappingDocument );
		this.jaxbSecondaryTableMapping = jaxbSecondaryTableMapping;
		this.joinTable = Helper.createTableSource(
				sourceMappingDocument,
				jaxbSecondaryTableMapping,
				inLineViewNameInferrer
		);

		if ( joinTable instanceof TableSource tableSource ) {
			if ( isEmpty( tableSource.getExplicitTableName() ) ) {
				throw new MappingException(
						String.format(
								Locale.ENGLISH,
								"Secondary table (<join/>) must explicitly name table or sub-select, but neither " +
										"specified for entity [%s]",
								entityNamingSource.getEntityName()
						),
						sourceMappingDocument.getOrigin()
				);
			}
		}

		if ( joinTable instanceof TableSource tableSource ) {
			logicalTableName = tableSource.getExplicitTableName();
		}
		else if ( joinTable instanceof InLineViewSource inLineViewSource ) {
			logicalTableName = inLineViewSource.getLogicalName();
		}
		else {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add table='secondary_table' naming the physical secondary table to the <join> element
  2. Or supply subselect='select ...' when the 'table' is actually a derived view
  3. Afterwards verify the <key> column(s) that link the secondary table to the primary

Example fix

// before
<join fetch='join'>
    <key column='id'/>
    <property name='bio' column='bio'/>
</join>

// after
<join table='person_detail' fetch='join'>
    <key column='person_id'/>
    <property name='bio' column='bio'/>
</join>
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: every <join> must name a table or a subselect
var joins = doc.getElementsByTagName("join");
for (int i = 0; i < joins.getLength(); i++) {
    var j = (org.w3c.dom.Element) joins.item(i);
    if (j.getAttribute("table").isEmpty() && j.getAttribute("subselect").isEmpty())
        throw new IllegalStateException("<join> without table or subselect under "
                + ((org.w3c.dom.Element) j.getParentNode()).getAttribute("name"));
}

Try / catch

catch (org.hibernate.boot.MappingException e) at Metadata build: the message names the entity - open its <join> and add table='...' (or subselect='...')

Prevention

When it happens

Trigger: A <join> element under a <class> or <joined-subclass> without a table attribute and without a subselect, e.g. <join fetch='join'><key column='id'/></join>.

Common situations: Dropping the table attribute during refactoring; expecting Hibernate to infer the secondary table name from the entity (it never does for <join>); attribute renamed or mistyped while editing.

Related errors


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