EQL Anti‐Patterns - fieldenms/tg GitHub Wiki
This EQL anti-pattern involves calling yieldAll() on a query whose source is another query (a source query) of an entity result type that declares calculated properties.
It looks harmless — "just give me everything back" — but it may fail at query-compilation time with Cannot resolve property [...].
The trap is subtle because of an asymmetry in how EQL treats a source query of an entity result type:
it exposes all of the entity's calculated properties for use in the enclosing query, but it exposes only the persistent properties that the source query actually yielded.
So yieldAll() dutifully tries to yield a calculated property whose underlying columns were never carried through — and the calculated property's expression has nothing to resolve against.
Consider a Vehicle entity that references the vehicle which superseded it (replacedBy), carries two monetary properties, and derives a total from them with a calculated property.
@KeyType(String.class)
@MapEntityTo
public class Vehicle extends AbstractEntity<String> {
@IsProperty
@MapTo
private Vehicle replacedBy;
@IsProperty
@MapTo
private Money listPrice;
@IsProperty
@MapTo
private Money purchasePrice;
@IsProperty
@Calculated("listPrice.amount + purchasePrice.amount")
private BigDecimal totalCost;
}Now suppose we build a source query yields only some of the persistent properties, and then wrap it in an enclosing query that pulls "everything" back out with yieldAll().
var sourceQuery = select(Vehicle.class)
.yield().prop("id").as("id")
.yield().prop("replacedBy").as("replacedBy")
.modelAsEntity(Vehicle.class);
// Select from the source query and yield everything.
var query = select(replacements)
.yieldAll()
.modelAsEntity(Vehicle.class);
co(Vehicle.class).getAllEntities(from(query).model()); // failsNote what the source query yields: id and replacedBy — and nothing else.
In particular, it does not yield listPrice or purchasePrice.
Executing query fails with Cannot resolve property [listPrice.amount].
Crucially, this is a compile-time failure, raised while EQL processes the query model — before any SQL is generated or sent to the database. It is not a data problem; the query is malformed and can never run.
yieldAll() does not mean "the columns this source query yields".
It means "all yielded properties + all calculated properties of the result type".
For a Vehicle-typed source, that set includes totalCost, which depends on listPrice and purchasePrice.
Because these persistent properties are not yielded by the source query, compilation fails.