hibernate/hibernate-orm · error · UnsupportedOperationException

Unsupported JdbcType nested in struct: {}

Error message

Unsupported JdbcType nested in struct: {}

What it means

When Hibernate serializes the attributes of a PostgreSQL composite/struct into the ROW(...) literal used for binding (AbstractPostgreSQLStructJdbcType.serializeJdbcValuesTo), it switches on each attribute's SQL type class. Only types handled by explicit cases (strings, numerics, booleans, nested STRUCTs, arrays, etc.) can be written; anything else falls through to the default branch and throws UnsupportedOperationException naming the unsupported JdbcType. The error means: one attribute of your @Struct mapping uses a JDBC type Hibernate cannot render inside a PostgreSQL struct literal.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java:1286

							}
						}

						appender.append( '}' );
						appender.quoteEnd();
					}
				}
				break;
			case SqlTypes.STRUCT:
				if ( subValue != null ) {
					final var structJdbcType = (AbstractPostgreSQLStructJdbcType) jdbcMapping.getJdbcType();
					appender.quoteStart();
					structJdbcType.serializeJdbcValuesTo( appender, options, (Object[]) subValue, '(' );
					appender.append( ')' );
					appender.quoteEnd();
				}
				break;
			default:
				throw new UnsupportedOperationException( "Unsupported JdbcType nested in struct: " + jdbcMapping.getJdbcType() );
		}
	}

	private StructAttributeValues getAttributeValues(
			EmbeddableMappingType embeddableMappingType,
			int[] orderMapping,
			Object[] rawJdbcValues,
			WrapperOptions options) throws SQLException {
		final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
		final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
		final StructAttributeValues attributeValues = new StructAttributeValues(
				numberOfAttributeMappings,
				orderMapping != null ?
						null :
						rawJdbcValues
		);
		int jdbcIndex = 0;
		for ( int i = 0; i < size; i++ ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Change the offending attribute to a type the serializer supports (String, Integer, Long, BigDecimal, Boolean, another @Struct embeddable, or an array of those).
  2. Convert the attribute to String via @Converter / @JdbcTypeCode(SqlTypes.VARCHAR) so the struct stores its text representation.
  3. Implement a custom JdbcType extending AbstractPostgreSQLStructJdbcType that overrides the serialization switch to handle your nested type.
  4. Move the exotic value out of the struct into its own column or side table.
  5. Check the Hibernate release notes - the set of nested types supported in struct literals grows across versions, so upgrade may alone fix it.

Example fix

// before
@Embeddable
@Struct(name = "address_type")
public class Address {
    private String city;        // OK
    private UUID tenantId;      // throws: Unsupported JdbcType nested in struct
}
// after
@Embeddable
@Struct(name = "address_type")
public class Address {
    private String city;
    @JdbcTypeCode(SqlTypes.VARCHAR)
    private String tenantId;    // store UUID as text inside the struct
}
Defensive patterns

Strategy: validation

Validate before calling

// At startup, verify every @Struct embeddable attribute maps to a serializer-supported Java type
static void checkStructAttributes(Class<?> embeddable) {
    Set<Class<?>> ok = Set.of(String.class, Integer.class, Long.class, BigDecimal.class, Boolean.class);
    for (Field f : embeddable.getDeclaredFields()) {
        Class<?> t = f.getType();
        boolean supported = ok.contains(t) || t.isAnnotationPresent(Struct.class) || t.isArray();
        if (!supported) throw new IllegalStateException(
            "@Struct " + embeddable.getSimpleName() + "." + f.getName()
            + " of type " + t + " is not serializable inside a PostgreSQL struct");
    }
}

Try / catch

catch (UnsupportedOperationException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Unsupported JdbcType nested in struct")) {
        // message names the JdbcType; map that attribute as String or implement a custom JdbcType
        throw new MappingConfigurationException(e.getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: An embeddable mapped with @Struct / SqlTypes.STRUCT on PostgreSQL containing an attribute whose JdbcType is outside the serializer's switch - typical culprits: UUID, JSON/JSONB, java.time types with bespoke JdbcTypes, custom enum JdbcTypes, BINARY/VARBINARY, INTERVAL. The exception fires on INSERT/UPDATE/flush, i.e. whenever the struct literal must be built and sent.

Common situations: Adopting PostgreSQL composite types (@Struct) and reusing an existing embeddable that contains a UUID or JSON attribute; mapping a domain object designed for embedded columns directly as a DB composite; upgrading Hibernate and having previously-tolerated attribute types now hit the default branch.

Related errors


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