hibernate/hibernate-orm · error · TreatException
Non-aggregate composite paths cannot be TREAT-ed
Error message
Non-aggregate composite paths cannot be TREAT-ed
What it means
NonAggregatedCompositeSimplePath represents the SQM path over the identity of an entity that uses a non-aggregated composite id — @IdClass style, or @Id placed directly on fields of an embedded shared with the entity rather than a standalone @EmbeddedId aggregate. The one-argument overload treatAs(Class) (declared to return SqmTreatedEntityValuedSimplePath) throws TreatException('Non-aggregate composite paths cannot be TREAT-ed') because the composite id has no type hierarchy of its own to downcast to: its runtime type is fixed by the mapping, so the TREAT is meaningless. It fires while the SQM tree is built (HQL parsing or criteria building), before any SQL is produced.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/domain/NonAggregatedCompositeSimplePath.java:63
getNavigablePathCopy( lhsCopy ),
getModel(),
lhsCopy,
nodeBuilder()
)
);
copyTo( path, context );
return path;
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitNonAggregatedCompositeValuedPath( this );
}
@Nonnull
@Override
public <S extends T> SqmTreatedEntityValuedSimplePath<T, S> treatAs(@Nonnull Class<S> treatJavaType) {
throw new TreatException( "Non-aggregate composite paths cannot be TREAT-ed" );
}
@Nonnull
@Override
public <S extends T> SqmTreatedSimplePath<T, S> treatAs(@Nonnull EntityDomainType<S> treatTarget) {
throw new TreatException( "Non-aggregate composite paths cannot be TREAT-ed" );
}
@Override
@Nonnull
public <S extends T> SqmTreatedSimplePath<T, S> treatAs(@Nonnull Class<S> treatJavaType, @Nullable String alias) {
throw new TreatException( "Non-aggregate composite paths cannot be TREAT-ed" );
}
@Override
@Nonnull
public <S extends T> SqmTreatedSimplePath<T, S> treatAs(@Nonnull EntityDomainType<S> treatTarget, @Nullable String alias) {
throw new TreatException( "Non-aggregate composite paths cannot be TREAT-ed" );View on GitHub (pinned to fad1729dce)
Solutions
- Drop the TREAT and navigate the component fields directly: o.id.branchCode in HQL, or root.get("id").get("branchCode") in criteria.
- If the intent was to downcast the entity, apply TREAT to the entity alias instead — treat(o as SubOrder) — then navigate its id fields.
- Compare the individual key columns in the predicate instead of downcasting the id.
- If subtype-specific id fields are genuinely required, switch to an @EmbeddedId (aggregated) mapping or add a real association — non-aggregated composite ids cannot model polymorphism.
Example fix
// before - Order uses a non-aggregated composite id
// HQL: from Order o where treat(o.id as BranchOrderPK).branchCode = 'X'
// -> TreatException: Non-aggregate composite paths cannot be TREAT-ed
// after - navigate the component fields, no downcast needed
// HQL: from Order o where o.id.branchCode = 'X'
query.where(cb.equal(root.get("id").get("branchCode"), "X")); Defensive patterns
Strategy: type-guard
Validate before calling
// composite (id-class) ids are never treatable: detect before building the query
jakarta.persistence.metamodel.EntityType<Order> order = root.getModel();
if (!order.hasSingleIdAttribute()) {
// @IdClass / non-aggregated composite id: forbid any treat on the id path
throw new IllegalArgumentException("Order has a non-aggregated composite id; navigate o.id fields directly");
} Type guard
static boolean hasNonAggregatedCompositeId(EntityType<?> entityType) {
return !entityType.hasSingleIdAttribute(); // false => @IdClass-style composite id
} Try / catch
try {
path.treatAs(SubType.class);
} catch (org.hibernate.query.sqm.TreatException e) {
// message: 'Non-aggregate composite paths cannot be TREAT-ed'
throw new QueryBuildException("TREAT not applicable to composite id path: " + e.getMessage(), e);
} Prevention
- Remember TREAT only applies to entity references in an inheritance hierarchy; ids and embeddables are never treatable.
- For @IdClass entities, navigate the id fields directly (o.id.branchCode).
- Flag 'treat(' near '.id' in HQL during code review.
- When a downcast was intended, put TREAT on the entity alias, not its identifier.
When it happens
Trigger: HQL 'from Order o where treat(o.id as SubIdClass).branchCode = :b' where Order's id is a non-aggregated composite (@IdClass or @Id-on-embeddable fields). Criteria equivalent: root.get("id").treat(SubIdClass.class) or ((JpaPath<?>) root.get("id")).treatAs(SubIdClass.class). Also reached when TREAT wraps a path derived from such an id after a join, e.g. treat(other.orderId as SubId).
Common situations: Legacy schemas with composite keys (@IdClass, @Id on embeddable fields); queries ported from single-column-key entities where TREAT on the root worked fine; template-generated criteria code that adds treat() to every path; inheritance hierarchies sharing a composite key where someone tries to reach subtype columns through the id.
Related errors
- Property '" + getPath( propertyHolder, inferredData ) + "' b
- Cannot apply TREAT operator to discriminator path
- Composite query parameter cannot be used in select
- Basic-value cannot be treated (downcast)
- Derived roots can not be treated
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/837edea778cc97b1.
Report an issue: GitHub.