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

  1. Remove the @Struct annotation - keep the embeddable as a plain aggregate/JSON-mapped component
  2. Or run against a database whose dialect supports user-defined types (PostgreSQL, Oracle, DB2)
  3. Or remap the attribute to a JSON column (@JdbcTypeCode(SqlTypes.JSON)) if the DB has no UDTs
  4. 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

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


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