hibernate/hibernate-orm · error · AnnotationException
Property '${property}' uses one-to-one mapping with mappedBy
Error message
Property '${property}' uses one-to-one mapping with mappedBy '${referencedPropertyName}' in the aggregate component class '${componentClassName}' within an array property, which is not allowed. What it means
AggregateComponentSecondPass.validateComponent walks every property of an embeddable that is mapped inside an array (inArray=true). Structs stored in arrays cannot own inverse sides, so any ToOne association that declares a mappedBy (getReferencedPropertyName() != null) - i.e. a bidirectional @OneToOne/@ManyToOne - is rejected with this AnnotationException.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java:191
subColumn
);
}
subColumn.setAssignmentExpression( assignmentExpression );
subColumn.setCustomRead( customReadExpression );
}
propertyHolder.getTable().getColumns().removeAll( aggregatedColumns );
}
private static void validateComponent(Component component, String basePath, boolean inArray) {
for ( Property property : component.getProperties() ) {
final Value value = property.getValue();
if ( value instanceof Component comp ) {
validateComponent( comp, qualify( basePath, property.getName() ), inArray );
}
else if ( value instanceof ToOne toOne ) {
if ( inArray && toOne.getReferencedPropertyName() != null ) {
throw new AnnotationException(
"Property '" + qualify( basePath, property.getName() )
+ "' uses one-to-one mapping with mappedBy '"
+ toOne.getReferencedPropertyName()
+ "' in the aggregate component class '"
+ component.getComponentClassName()
+ "' within an array property, which is not allowed."
);
}
}
else if ( value instanceof Collection collection ) {
if ( inArray && collection.getMappedByProperty() != null ) {
throw new AnnotationException(
"Property '" + qualify( basePath, property.getName() )
+ "' uses *-to-many mapping with mappedBy '"
+ collection.getMappedByProperty()
+ "' in the aggregate component class '"
+ component.getComponentClassName()
+ "' within an array property, which is not allowed."View on GitHub (pinned to fad1729dce)
Solutions
- Remove mappedBy from the association inside the embeddable so it is unidirectional
- Better: move the association out of the aggregate embeddable onto the owning entity
- Replace the association with a plain FK column stored in the struct if ownership info is needed
- Restructure so the embeddable is not used within an array (map as separate table/entity)
Example fix
// before: bidirectional to-one inside an aggregate that is used in an array
@Embeddable
public class ContactInfo {
@OneToOne(mappedBy = "contactInfo") // rejected inside array aggregates
private Person person;
}
@Entity
public class Customer {
@Array
private List<ContactInfo> contacts;
}
// after: keep the aggregate association-free; own the link from the entity side
@Embeddable
public class ContactInfo { /* value fields only */ }
@Entity
public class Person {
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
} Defensive patterns
Strategy: validation
Validate before calling
// Before boot: reject aggregates destined for arrays that contain mappedBy associations
static boolean aggregateIsArraySafe(Class<?> embeddable) {
for (Field f : embeddable.getDeclaredFields()) {
OneToOne o2o = f.getAnnotation(OneToOne.class);
ManyToOne m2o = f.getAnnotation(ManyToOne.class);
if ((o2o != null && !o2o.mappedBy().isEmpty())
|| (m2o != null && !m2o.mappedBy().isEmpty())) {
return false;
}
if (f.getType().isAnnotationPresent(Embeddable.class)
&& !aggregateIsArraySafe(f.getType())) {
return false;
}
}
return true;
} Try / catch
try {
final SessionFactory sf = new MetadataSources(standardServiceRegistry)
.addAnnotatedClass(MyEntity.class)
.buildMetadata()
.buildSessionFactory();
} catch (AnnotationException | MappingException e) {
throw new IllegalStateException("Invalid ORM mapping, aborting startup: " + e.getMessage(), e);
} Prevention
- Treat aggregate embeddables as pure value types: no mappedBy, no collection tables
- Keep bidirectionality on entity classes, never inside structs
- Add a mapping smoke test for every embeddable reused inside arrays
When it happens
Trigger: An embeddable (directly or nested) used inside an @Array/element-collection-of-aggregates contains @OneToOne(mappedBy = ...) (or any to-one with a referenced property), and the whole component is stored within an array property of an entity.
Common situations: Reusing an existing embeddable that worked as a plain @Embedded attribute inside a new array aggregate; making a struct bidirectional by adding mappedBy; Hibernate 6->7 migration where aggregate validation became stricter.
Related errors
- Property '${property}' uses *-to-many mapping with mappedBy
- Dialect does not support structured array types: ${dialectCl
- Property '${property}' defines a collection table '${collect
- Summarization is not supported by DBMS
- Database does not support user-defined types (remove '@Struc
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/e6bb821d20a53d48.
Report an issue: GitHub.