-
Notifications
You must be signed in to change notification settings - Fork 6
EQL Anti‐Patterns
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.
Per aspera ad astra
- Web UI Design and Web API
- Safe Communication and User Authentication
- Gitworkflow
- JavaScript: Testing with Maven
- Java Application Profiling
-
TG Development Guidelines
- TG Releases
- Domain-Driven Testing Guide
- TLS and HAProxy for development
- TG Development Checklist
- Entity
- Entities and their validation
- Entity Properties
- Entity Type Enhancement
- EQL
- Tooltip How To
- All about Matchers
- All about Fetch Models
- Streaming data
- Synthetic entities
- Activatable entities
- Save with fetch
- Jasper Reports
- Opening Compound Master from another Compound Master
- Window management test plan
- Multi Time Zone Environment
- GraphQL Web API
- Guice
- Maven
- RichText Data Migration
- Full Text Search
- Deployment recipes
- Application Configuration
- Observability
- JRebel Installation and Integration
- Compile-time mechanisms
- Work in progress