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
- Take the identifier from the message and grep for it across @Table/@SequenceGenerator annotations and XML mappings to find the duplicate definitions.
- Remove or rename the duplicate mapping (one owning entity per table; one generator per sequence name).
- If two classes must read the same data, map a database view under a distinct name instead of the table twice.
- 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
- One owning entity per physical table; expose extra read models via database views with distinct names
- Keep a single mapping source (annotations OR xml) and audit persistence.xml for duplicate <class> entries
- Check for duplicated jars/classes on the classpath when entity scans register mappings twice
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
- Error performing schema management [persistence unit: {}]
- Attempt to resolve foreign key metadata from JDBC metadata f
- Attempt to resolve JDBC metadata failed to find columns for
- Error resolving legacy import resource : %s
- Schema validation: missing table [%s]
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/36d85afe00734898.
Report an issue: GitHub.