hibernate/hibernate-orm · error · MappingException
Struct [%s] is defined by multiple components %s with differ
Error message
Struct [%s] is defined by multiple components %s with different number of mappings %d and %d
What it means
When multiple components map to the same database struct name, Hibernate enforces that they describe the identical type. validateEqual first compares column counts; if two definitions of the same struct map different numbers of columns, this MappingException reports the struct name, the involved component classes, and both counts.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java:394
}
private static void ensureParentInitialized(
InFlightMetadataCollector metadataCollector,
AggregateColumn aggregateColumn) {
do {
// Trigger resolving of the value so that the column gets properly filled
aggregateColumn.getValue().getType();
// Make sure this state is initialized
aggregateColumn.getSqlTypeCode( metadataCollector );
aggregateColumn.getSqlType( metadataCollector );
aggregateColumn = aggregateColumn.getComponent().getParentAggregateColumn();
} while ( aggregateColumn != null );
}
private void validateEqual(UserDefinedObjectType udt1, UserDefinedObjectType udt2) {
if ( udt1.getColumnSpan() != udt2.getColumnSpan() ) {
throw new MappingException(
String.format(
"Struct [%s] is defined by multiple components %s with different number of mappings %d and %d",
udt1.getName(),
findComponentClasses(),
udt1.getColumnSpan(),
udt2.getColumnSpan()
)
);
}
final List<Column> missingColumns = new ArrayList<>();
for ( Column column1 : udt1.getColumns() ) {
final Column column2 = udt2.getColumn( column1 );
if ( column2 == null ) {
missingColumns.add( column1 );
}
else if ( !column1.getSqlType().equals( column2.getSqlType() ) ) {
throw new MappingException(View on GitHub (pinned to fad1729dce)
Solutions
- Make the column sets identical across all components sharing the struct name
- If the types genuinely differ, give one of them a different struct name
- Delete the stale/duplicate embeddable mapping
- After fixing, ensure only one canonical Java class represents each database UDT
Example fix
// before: same struct name, different column counts
@Embeddable @Struct(name = "address")
public class AddressV1 { String street; String city; } // 2 columns
@Embeddable @Struct(name = "address")
public class AddressV2 { String street; String city; String zip; } // 3 columns -> error
// after: one canonical definition (or distinct struct names)
@Embeddable @Struct(name = "address")
public class Address { String street; String city; String zip; } // single source of truth Defensive patterns
Strategy: validation
Validate before calling
// Before boot: embeddables sharing a struct name must map the same number of columns
static Map<String, List<Class<?>>> structFieldCounts(List<Class<?>> embeddables) {
Map<String, List<Class<?>>> byStruct = new HashMap<>();
for (Class<?> c : embeddables) {
Struct s = c.getAnnotation(Struct.class);
if (s != null) byStruct.computeIfAbsent(s.name(), k -> new ArrayList<>()).add(c);
}
return byStruct; // assert each list's members all map equal column counts
} Try / catch
try {
sessionFactory = metadata.buildSessionFactory();
} catch (MappingException e) {
if (e.getMessage().contains("different number of mappings")) {
throw new IllegalStateException("Two embeddables share a struct name but map different columns", e);
}
throw e;
} Prevention
- One Java class per database struct type - no parallel variants
- Search the codebase for @Struct(name = "x") duplicates whenever you add or edit one
- Cover struct mappings with a boot smoke test in CI
When it happens
Trigger: Two (or more) embeddable classes, or two mappings of embeddables, carry the same @Struct(name = ...) but declare different numbers of persistent attributes/columns - e.g. one Address struct with 3 fields and another with 4.
Common situations: Copy-pasting an embeddable and adding a field to only one copy; different teams mapping the same database UDT with diverging Java models; a stale duplicate embeddable left behind after refactoring.
Related errors
- Database does not support user-defined types (remove '@Struc
- Struct [%s] of class [%s] is defined by multiple components
- Struct [%s] is defined by multiple components %s but some co
- Dialect does not support structured array types: ${dialectCl
- Property '${property}' uses one-to-one mapping with mappedBy
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/857391a77967c1c9.
Report an issue: GitHub.