hibernate/hibernate-orm · error · SchemaManagementException

Export identifier [%s] encountered more than once

Error message

Export identifier [%s] encountered more than once

What it means

SchemaManagementException from AbstractSchemaMigrator.checkExportIdentifier: during schema migration, two exportable schema objects (tables, sequences) produced the same export identifier (typically schema.table or the sequence name). The migrator keeps a Set of identifiers so each object is migrated exactly once; a duplicate means the same database object is represented twice in the Metadata model.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaMigrator.java:491

	boolean equivalentForeignKeyExistsInDatabase(TableInformation tableInformation, String referencingColumn, String referencedTable) {
		return StreamSupport.stream( tableInformation.getForeignKeys().spliterator(), false )
				.flatMap( foreignKeyInformation -> StreamSupport.stream( foreignKeyInformation.getColumnReferenceMappings().spliterator(), false ) )
				.anyMatch( columnReferenceMapping -> {
			final var referencingColumnMetadata = columnReferenceMapping.getReferencingColumnMetadata();
			final var referencedColumnMetadata = columnReferenceMapping.getReferencedColumnMetadata();
			final String existingReferencingColumn = referencingColumnMetadata.getColumnIdentifier().getText();
			final String existingReferencedTable =
					referencedColumnMetadata.getContainingTableInformation().getName().getTableName().getCanonicalName();
			return referencingColumn.equalsIgnoreCase( existingReferencingColumn )
				&& referencedTable.equalsIgnoreCase( existingReferencedTable );
		} );
	}

	protected void checkExportIdentifier(Exportable exportable, Set<String> exportIdentifiers) {
		final String exportIdentifier = exportable.getExportIdentifier();
		if ( exportIdentifiers.contains( exportIdentifier ) ) {
			throw new SchemaManagementException(
					String.format("Export identifier [%s] encountered more than once", exportIdentifier )
			);
		}
		exportIdentifiers.add( exportIdentifier );
	}

	protected static void applySqlStrings(
			boolean quiet,
			String[] sqlStrings,
			Formatter formatter,
			ExecutionOptions options,
			GenerationTarget... targets) {
		if ( sqlStrings != null ) {
			for ( String sql : sqlStrings ) {
				applySqlString( quiet, sql, formatter, options, targets );
			}
		}
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Take the identifier from the message and grep for it across @Table/@SequenceGenerator annotations and XML mappings to find the duplicate definitions.
  2. Remove or rename the duplicate mapping (one owning entity per table; one generator per sequence name).
  3. If two classes must read the same data, map a database view under a distinct name instead of the table twice.
  4. Check the classpath for duplicated jars/classes and persistence.xml for double <class> entries.

Example fix

// before: two entities mapped to the same table -> duplicate export identifier
@Entity @Table(name = "customer")
public class Customer { ... }

@Entity @Table(name = "customer")
public class Client { ... }

// after: one owning entity; expose the second view via a mapped DB view
@Entity @Table(name = "customer")
public class Customer { ... }

@Entity @Table(name = "client_view") // CREATE VIEW client_view AS SELECT ...
public class Client { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Before SchemaUpdate, assert every exportable in the model has a unique export identifier
Set<String> seen = new HashSet<>();
metadata.getDatabase().getNamespaces().forEach(ns -> {
    ns.getTables().forEach(t -> {
        if (!seen.add(t.getExportIdentifier())) {
            throw new IllegalStateException("Duplicate export identifier: " + t.getExportIdentifier());
        }
    });
    ns.getSequences().forEach(s -> {
        if (!seen.add(s.getExportIdentifier())) {
            throw new IllegalStateException("Duplicate export identifier: " + s.getExportIdentifier());
        }
    });
});

Try / catch

try {
    new SchemaUpdate(metadata, registry).execute(EnumSet.of(TargetType.DATABASE), ...);
} catch (SchemaManagementException e) {
    if (e.getMessage() != null && e.getMessage().contains("encountered more than once")) {
        // grep the reported identifier across @Table/@SequenceGenerator and mapping XML to find the duplicate registration
    }
    throw e;
}

Prevention

When it happens

Trigger: Running SchemaUpdate (hibernate.hbm2ddl.auto=update) when the mapped model contains the same object twice: two @Entity classes mapped to the same @Table name in the same schema, an entity registered twice in persistence.xml/hibernate.cfg.xml, a table mapped both by an entity and by a leftover hbm.xml or <join>, or two sequence definitions (@SequenceGenerator with the same sequenceName in different entities).

Common situations: Copy-pasted entity left behind in another package after a refactor; persistence.xml listing a class both explicitly and via auto-scan; the same jar duplicated on the classpath so mappings register twice; two modules mapping one legacy table; a name collision differing only in quoting/case.

Related errors


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