hibernate/hibernate-orm · error · IllegalArgumentException

Illegal call to IdentifiableType#getId for class [{}] define

Error message

Illegal call to IdentifiableType#getId for class [{}] defined with @IdClass

What it means

JPA's IdentifiableType#getId(Y) only supports a single id attribute or an aggregated @EmbeddedId. When the entity uses @IdClass (a non-aggregated composite id made of several @Id fields), there is no single id attribute to return, so Hibernate throws IllegalArgumentException. This mirrors the behavior mandated by the JPA specification.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractIdentifiableType.java:135

		return getSuperType();
	}

	@Override
	@Nonnull
	public <Y> SqmSingularPersistentAttribute<? super J, Y> getId(Class<Y> javaType) {
		ensureNoIdClass();
		final var id = findIdAttribute();
		if ( id != null ) {
			checkType( id, javaType );
		}
		@SuppressWarnings("unchecked") // safe, we just checked
		final var castId = (SqmSingularPersistentAttribute<? super J, Y>) id;
		return castId;
	}

	private void ensureNoIdClass() {
		if ( hasIdClass() ) {
			throw new IllegalArgumentException(
					"Illegal call to IdentifiableType#getId for class [" + getTypeName() + "] defined with @IdClass"
			);
		}
	}


	@Override
	public @Nullable SqmSingularPersistentAttribute<? super J, ?> findIdAttribute() {
		if ( id != null ) {
			return id;
		}
		else if ( getSuperType() != null ) {
			return getSuperType().findIdAttribute();
		}
		else {
			return null;
		}
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use getIdClassAttributes() (or visitIdClassAttributes) to obtain the set of id attributes for @IdClass entities
  2. Switch the entity to a single @Id or an @EmbeddedId if your framework requires getId(); @EmbeddedId supports getId() via the embedded attribute
  3. Branch first: if (entityType.hasIdClass()) ... else entityType.getId(...) — hasIdClass() is the cheap guard

Example fix

// before
SingularAttribute<? super Order, Long> id = orderType.getId(Long.class); // @IdClass -> throws

// after
if (orderType.hasIdClass()) {
    Set<SingularAttribute<? super Order,?>> parts = orderType.getIdClassAttributes();
} else {
    SingularAttribute<? super Order, Long> id = orderType.getId(Long.class);
}
Defensive patterns

Strategy: validation

Validate before calling

if (identifiableType.hasIdClass()) {
    Set<SingularAttribute<? super E, ?>> parts = identifiableType.getIdClassAttributes();
} else {
    SingularAttribute<? super E, ?> id = identifiableType.getId(idJavaType(identifiableType));
}

Try / catch

try {
    return identifiableType.getId(type);
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("defined with @IdClass")) {
        return null; // caller must use getIdClassAttributes()
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling entityType.getId(javaType) or getDeclaredId(javaType) on a ManagedType whose entity class is annotated with @IdClass(MyIdClass.class). Common in generic DAO frameworks that auto-resolve 'the id attribute' for every entity.

Common situations: Generic repository/base-entity code that assumes every entity has exactly one @Id. Legacy schemas with composite keys modeled via @IdClass. Migrating code from a single-key entity to a composite key without updating the id-access utilities.

Related errors


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