hibernate/hibernate-orm · critical · MappingException

Unable to resolve mapped-by path : (%s) %s

Error message

Unable to resolve mapped-by path : (%s) %s

What it means

AbstractCollectionPersister resolves a mappedBy property path by walking the target entity's attribute mappings token-by-token (source.findAttributeMapping(partName), descending through ManagedMappingType). If a segment is missing or an intermediate segment is not a managed type, resolution fails with MappingException naming the entity and the full mappedBy path.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java:672

	private static AttributeMapping resolveMappedBy(EntityPersister entityPersister, String mappedByProperty) {
		final var propertyPathParts = new StringTokenizer( mappedByProperty, ".", false );
		final int tokenCount = propertyPathParts.countTokens();
		assert tokenCount > 0;
		if ( tokenCount == 1 ) {
			return entityPersister.findAttributeMapping( propertyPathParts.nextToken() );
		}
		else {
			ManagedMappingType source = entityPersister;
			while ( propertyPathParts.hasMoreTokens() ) {
				final String partName = propertyPathParts.nextToken();
				final var namedPart = source.findAttributeMapping( partName );
				if ( !propertyPathParts.hasMoreTokens() ) {
					return namedPart;
				}
				source = (ManagedMappingType) namedPart.getPartMappingType();
			}
			throw new MappingException(
					String.format(
							Locale.ROOT,
							"Unable to resolve mapped-by path : (%s) %s",
							entityPersister.getEntityName(),
							mappedByProperty
					)
			);
		}
	}

	private BeforeExecutionGenerator createGenerator(RuntimeModelCreationContext context, IdentifierCollection collection) {
		final Generator generator =
				collection.getIdentifier()
						.createGenerator( context.getDialect(), null, null, context.getGeneratorSettings() );
		if ( generator.generatedOnExecution() ) {
			throw new MappingException("must be an BeforeExecutionGenerator"); //TODO fix message
		}
		return (BeforeExecutionGenerator) generator;

View on GitHub (pinned to fad1729dce)

Solutions

  1. Verify the mappedBy property exists verbatim on the referenced entity class (check spelling and get/set pairs)
  2. For dotted paths, check each segment: intermediate segments must be embeddable-valued attributes, final segment is the mapped attribute
  3. Ensure mappedBy is on the inverse (non-owning) side and matches the owning side's @JoinColumn/property
  4. After refactors, grep for mappedBy values and keep them in sync with the renamed property

Example fix

// before
@Entity public class Order {
  @OneToMany(mappedBy = "cust") // no property 'cust' on Customer
  private List<OrderLine> lines;
}

// after
@Entity public class Order {
  @OneToMany(mappedBy = "order") // exact property on OrderLine
  private List<OrderLine> lines;
}
Defensive patterns

Strategy: validation

Validate before calling

// Verify every mappedBy string resolves on the target entity (supports dotted embeddable paths)
static void checkMappedBy(Class<?> targetEntity, String mappedBy) throws Exception {
  Class<?> current = targetEntity;
  String[] parts = mappedBy.split("\\.");
  for (int i = 0; i < parts.length; i++) {
    java.lang.reflect.Field f = Stream.of(current.getDeclaredFields())
        .filter(x -> x.getName().equals(parts[i])).findFirst()
        .orElseThrow(() -> new IllegalStateException(
            "mappedBy '" + mappedBy + "': no property '" + parts[i] + "' on " + current.getName()));
    current = f.getType(); // intermediate segments must be embeddable-valued in the real mapping
  }
}

Try / catch

try {
  sessionFactory = new Configuration().addAnnotatedClass(Customer.class).buildSessionFactory();
} catch (MappingException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("Unable to resolve mapped-by path")) {
    throw new IllegalStateException("A mappedBy value points at a nonexistent property — verify the inverse-side attribute name", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: @OneToMany(mappedBy="x") / @ManyToMany(mappedBy="x") where property x does not exist on the referenced entity; a dotted mappedBy path ('address.city') whose intermediate segment is an embeddable attribute that does not exist or is not embeddable; the path's first token does not match any attribute mapping.

Common situations: Rename refactors of the inverse-side property leave mappedBy strings stale; mappedBy copy-pasted from the other side of the association (pointing at itself); dotted paths into @Embedded targets with segments in the wrong order; mappedBy placed on the owning side instead of the inverse side.

Related errors


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