hibernate/hibernate-orm · error · MappingException
Database does not support user-defined types (remove '@Struc
Error message
Database does not support user-defined types (remove '@Struct' annotation)
What it means
Hibernate maps an @Struct-annotated embeddable to a database user-defined type (UDT). During the AggregateComponentSecondPass it asks the dialect supportsUserDefinedTypes(); on dialects that report no UDT support (the MySQL/MariaDB family is typical) it throws this MappingException, explicitly telling you to remove the '@Struct' annotation.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java:90
// Sort the component properties early to ensure the aggregated
// columns respect the same order as the component's properties
final int[] originalOrder = component.sortProperties();
// Compute aggregated columns since we have to replace them in the table with the aggregate column
final List<Column> aggregatedColumns = component.getAggregatedColumns();
final AggregateColumn aggregateColumn = component.getAggregateColumn();
ensureInitialized( metadataCollector, aggregateColumn );
validateSupportedColumnTypes( propertyHolder.getPath(), component );
final QualifiedName structName = component.getStructName();
final boolean addAuxiliaryObjects;
if ( structName != null ) {
final Namespace namespace = database.locateNamespace(
structName.getCatalogName(),
structName.getSchemaName()
);
if ( !database.getDialect().supportsUserDefinedTypes() ) {
throw new MappingException( "Database does not support user-defined types (remove '@Struct' annotation)" );
}
final var udt = new UserDefinedObjectType( "orm", namespace, structName.getObjectName() );
for ( var aggregatedColumn : aggregatedColumns ) {
udt.addColumn( aggregatedColumn );
}
final var registeredUdt = namespace.createUserDefinedType( structName.getObjectName(), name -> udt );
if ( registeredUdt == udt ) {
addAuxiliaryObjects = true;
orderColumns( registeredUdt, originalOrder );
}
else {
addAuxiliaryObjects =
isAggregateArray()
&& namespace.locateUserDefinedArrayType( Identifier.toIdentifier( aggregateColumn.getSqlType() ) ) == null;
validateEqual( registeredUdt, udt );
}
}
else {View on GitHub (pinned to fad1729dce)
Solutions
- Remove the @Struct annotation - keep the embeddable as a plain aggregate/JSON-mapped component
- Or run against a database whose dialect supports user-defined types (PostgreSQL, Oracle, DB2)
- Or remap the attribute to a JSON column (@JdbcTypeCode(SqlTypes.JSON)) if the DB has no UDTs
- Verify you did not accidentally force a wrong dialect via hibernate.dialect or a wrong JDBC URL
Example fix
// before: struct on a UDT-less database (MySQL/MariaDB)
@Embeddable
@Struct(name = "address")
public class Address { ... }
// after: plain aggregate mapped as JSON on such databases
@Embeddable
public class Address { ... }
@Entity
public class Customer {
@JdbcTypeCode(SqlTypes.JSON)
private Address address; // no UDT required Defensive patterns
Strategy: validation
Validate before calling
// Before boot: check the dialect capability for every @Struct embeddable in the model
static boolean structMappingsSupported(Dialect dialect) {
return dialect.supportsUserDefinedTypes();
}
// In multi-DB test suites, skip struct cases on incapable databases
Assume.assumeTrue(structMappingsSupported(dialect)); Try / catch
try {
sessionFactory = metadata.buildSessionFactory();
} catch (MappingException e) {
if (e.getMessage().contains("remove '@Struct'")) {
// database cannot host UDTs: fall back to a JSON/plain-embeddable variant of the model
throw new IllegalStateException("Model uses @Struct but DB lacks user-defined types", e);
}
throw e;
} Prevention
- Keep one mapping variant per database capability, selected by profile
- Check dialect.supportsUserDefinedTypes() in a boot-time guard for models containing @Struct
- Prefer JSON aggregate mappings when the deployment must stay portable across MySQL-like databases
When it happens
Trigger: An @Embeddable class annotated with @Struct (or an aggregate mapping that resolves a struct name) is referenced by an entity while the configured dialect's supportsUserDefinedTypes() returns false - e.g. running the same entities on MySQL that were written for PostgreSQL.
Common situations: Portability testing across databases (PostgreSQL in prod, MySQL/H2 variants in CI); switching dialects without revisiting struct mappings; new @Struct usage added against the wrong database profile.
Related errors
- Dialect does not support structured array types: ${dialectCl
- Struct [%s] is defined by multiple components %s with differ
- Struct [%s] of class [%s] is defined by multiple components
- Struct [%s] is defined by multiple components %s but some co
- Error creating SQL create commands for UDT :
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/79e0e5794408a290.
Report an issue: GitHub.