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
- Verify the mappedBy property exists verbatim on the referenced entity class (check spelling and get/set pairs)
- For dotted paths, check each segment: intermediate segments must be embeddable-valued attributes, final segment is the mapped attribute
- Ensure mappedBy is on the inverse (non-owning) side and matches the owning side's @JoinColumn/property
- 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
- Treat mappedBy strings like API contracts: update them in the same commit as property renames
- Keep mappedBy on the inverse side only, referencing the owning side's exact property name
- For dotted mappedBy into embeddables, test each segment resolves at startup with the scan above
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
- Property '${property}' uses *-to-many mapping with mappedBy
- Attribute was not a Map : ${collectionMemberType}
- Unable to create AttributeConverter instance
- Property '${property}' uses one-to-one mapping with mappedBy
- Native temporal exclusion column option is not supported by
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/92a53af9646fab23.
Report an issue: GitHub.