hibernate/hibernate-orm · error · PropertyNotFoundException

Could not resolve attribute '{}' of '{}'

Error message

Could not resolve attribute '{}' of '{}'

What it means

When the JPA metamodel is built for an entity with an IdClass (VIRTUAL identifier nature), AttributeFactory.resolveVirtualIdentifierMember looks up each IdClass property inside the CompositeIdentifierMapping's embeddable. If no attribute mapping with the property's name exists, Hibernate throws PropertyNotFoundException naming the attribute and the IdClass Java type. It almost always means the IdClass field names no longer line up with the entity's @Id property names.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java:673

	private static final MemberResolver virtualIdentifierMemberResolver = (attributeContext, metadataContext) -> {
		final var identifiableType = (AbstractIdentifiableType<?>) attributeContext.getOwnerType();
		final var declaringEntity = getDeclaringEntity( identifiableType, metadataContext );
		return resolveVirtualIdentifierMember( attributeContext.getPropertyMapping(), declaringEntity );
	};

	private static Member resolveVirtualIdentifierMember( Property property, EntityPersister entityPersister) {
		final var identifierMapping = entityPersister.getIdentifierMapping();
		if ( identifierMapping.getNature() != EntityIdentifierMapping.Nature.VIRTUAL ) {
			throw new IllegalArgumentException( "expecting IdClass mapping" );
		}

		final var cid = (CompositeIdentifierMapping) identifierMapping;
		final var embeddable = cid.getPartMappingType();
		final String attributeName = property.getName();
		final var attributeMapping = embeddable.findAttributeMapping( attributeName );
		if ( attributeMapping == null ) {
			throw new PropertyNotFoundException(
					"Could not resolve attribute '" + attributeName
							+ "' of '" + embeddable.getJavaType().getJavaTypeClass().getName() + "'"
			);
		}

		final Getter getter = attributeMapping.getPropertyAccess().getGetter();
		return getter instanceof PropertyAccessMapImpl.GetterImpl
				? new MapMember( attributeName, property.getType().getReturnedClass() )
				: getter.getMember();
	}

	/**
	 * A {@link Member} resolver for normal attributes.
	 */
	private static final MemberResolver normalMemberResolver = (attributeContext, metadataContext) -> {
		final var ownerType = attributeContext.getOwnerType();
		final Property property = attributeContext.getPropertyMapping();
		final var persistenceType = ownerType.getPersistenceType();

View on GitHub (pinned to fad1729dce)

Solutions

  1. Diff the IdClass fields against the entity's @Id properties — every IdClass field must have a same-named entity @Id property (exact name, same type).
  2. Fix mismatches: rename the IdClass field or the entity property so both sides match.
  3. Prefer @EmbeddedId or @EmbeddedId + @MapsId composite key patterns when possible — they tie the key fields to one embeddable and avoid this drift class of bugs.
  4. Add a startup test that touches EntityManagerFactory.getMetamodel() for every entity so name drift fails CI, not production.

Example fix

// before
public class OrderLinePK implements Serializable {
    private Long order;     // entity property is named "orderId"
    private Long product;
}
@IdClass(OrderLinePK.class)
public class OrderLine {
    @Id private Long orderId;
    @Id private Long product;
}

// after
public class OrderLinePK implements Serializable {
    private Long orderId;   // matches entity property name
    private Long product;
}
Defensive patterns

Strategy: validation

Validate before calling

// verify IdClass field names match the entity's @Id property names before bootstrap
static void checkIdClass(Class<?> entity, Class<?> idClass) {
    Set<String> ids = Arrays.stream(entity.getDeclaredFields())
            .filter(f -> f.isAnnotationPresent(Id.class))
            .map(Field::getName).collect(Collectors.toSet());
    for (Field f : idClass.getDeclaredFields()) {
        if (!ids.contains(f.getName()))
            throw new IllegalStateException("IdClass field not an @Id property: " + f.getName());
    }
}

Type guard

static boolean idClassMatches(Class<?> entity, Class<?> idClass) {
    Set<String> ids = Arrays.stream(entity.getDeclaredFields())
            .filter(f -> f.isAnnotationPresent(Id.class))
            .map(Field::getName).collect(Collectors.toSet());
    return Arrays.stream(idClass.getDeclaredFields()).map(Field::getName).allMatch(ids::contains);
}

Try / catch

try {
    emf.getMetamodel().entity(OrderLine.class);
} catch (PropertyNotFoundException e) {
    if (e.getMessage().startsWith("Could not resolve attribute")) {
        // IdClass/embeddable field drift — align names
    }
    throw e;
}

Prevention

When it happens

Trigger: Building the JPA metamodel or a Criteria query for an @IdClass entity where the IdClass declares a field that is not one of the entity's @Id properties (or names differ by case/typo); an IdClass field removed from the entity but kept in the IdClass; property names renamed by refactoring only on one side.

Common situations: @IdClass + @Id derived identities (legacy pre-@MapsId pattern, e.g. JPA 1.0 era OrderLinePK); renames via IDE refactor updating entity but not the IdClass; copy-pasted IdClasses between entities; cases where the embeddable mapping is filtered (e.g. property marked @Transient or grouped into a subclass) while the IdClass still references it.

Related errors


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