Help us improve
Share bugs, ideas, or general feedback.
From apache-cayenne
Writes and modifies Cayenne 5.0 queries: ObjectSelect, SQLSelect, SelectById, prefetching, pagination, aggregates, and raw SQL. Relevant when fetching entities by criteria or avoiding N+1.
npx claudepluginhub apache/cayenne --plugin apache-cayenneHow this skill is triggered — by the user, by Claude, or both
Slash command
/apache-cayenne:cayenne-queryThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!--
Edits Cayenne ORM model XML files — DataMaps and project descriptors — to add or modify entities, attributes, relationships, embeddables, named queries, stored procedures, and DataNodes.
Generates optimized SQL queries for PostgreSQL, MySQL, SQLite and NoSQL for MongoDB, DynamoDB, Redis; supports ORMs like Prisma. Explains plans, indexes, and performance optimizations.
Generates CQRS query handlers, validators, and response DTOs using Dapper and MediatR. Useful for implementing read-optimized .NET applications with explicit separation of commands and queries.
Share bugs, ideas, or general feedback.
Write idiomatic Cayenne 5.0 queries — ObjectSelect, SelectById, SQLSelect, expressions, prefetch, pagination, aggregates.
${CLAUDE_PLUGIN_ROOT}/references/query-api.md — every query mechanism with examples (ObjectSelect, SQLSelect/SQLExec, Expression, ColumnSelect, SelectById).| Intent | Use |
|---|---|
| Fetch one or more entities matching criteria | ObjectSelect.query(Cls.class).where(...).select(ctx) |
| Fetch by primary key | SelectById.query(Cls.class, pk).selectOne(ctx) |
| Aggregate (count, sum) | ObjectSelect.query(Cls.class).selectCount(ctx) or ColumnSelect with aggregate functions |
| One or a few columns only (DTO-style) | ObjectSelect.columnQuery(Cls.class, Cls.NAME, Cls.AGE).select(ctx) |
| Raw SQL with parameter binding | SQLSelect.query(Cls.class, "SELECT ...").params(...).select(ctx) |
| Insert/update/delete bulk | SQLExec.query("UPDATE ...").update(ctx) |
| Reused named query stored in DataMap | XML <query> (see ${CLAUDE_PLUGIN_ROOT}/references/datamap-schema.md) loaded via NamedQuery |
SelectByIdWhen the user is fetching a single entity by its PK, use SelectById rather than ObjectSelect.where(PK.eq(...)):
Artist a = SelectById.query(Artist.class, 42).selectOne(ctx);
SelectById can hit the ObjectContext's session cache without firing a SQL query when the row is already loaded. ObjectSelect.where(...) always goes to the database. For composite PKs, pass a Map<String, Object> of PK column → value.
Property constantscgen generates Property<T> constants on each entity superclass. Use them — they're type-checked at compile time:
ObjectSelect.query(Artist.class)
.where(Artist.ARTIST_NAME.likeIgnoreCase("p%"))
.and(Artist.DATE_OF_BIRTH.between(d1, d2))
.select(ctx);
Only fall back to ExpressionFactory.matchExp("artistName", ...) or Expression.fromString(...) when:
query-api.md lists all common predicate methods on Property.
When the query traverses a relationship in a loop, always add a prefetch to avoid N+1:
// Bad — fires one query per artist when paintings are accessed
for (Artist a : artists) { a.getPaintings().size(); }
// Good
List<Artist> artists = ObjectSelect.query(Artist.class)
.prefetch(Artist.PAINTINGS.disjoint())
.select(ctx);
Pick:
.joint() — to-one relationships, or to-many with small fan-out. Single SQL join..disjoint() — to-many, modest size. Two queries..disjointById() — to-many, very large parent set. Two queries, keyed by PKs.ObjectSelect.query(Artist.class)
.orderBy(Artist.ARTIST_NAME.asc())
.pageSize(50) // server-side pagination
.limit(1000)
.localCache() // per-context cache, or .sharedCache("group-name")
.select(ctx);
Use sharedCache for reference data (rarely changes, read often). Use pageSize to avoid loading entire result sets.
List<Artist> hits = SQLSelect.query(Artist.class,
"SELECT * FROM ARTIST WHERE ARTIST_NAME LIKE #bind($pattern)")
.params(Map.of("pattern", userInput + "%"))
.select(ctx);
Always use #bind($name) placeholders. Never concatenate user input into SQL. Cayenne's SQLTemplate is Velocity-based — see query-api.md for #bind, #bindEqual, #chain, and adapter-specific SQL with <sql adapter-class="...">.
#bind($name). Concatenation is a SQL injection vector and may also result in invalid syntax.selectOne when multiple may match. It throws. Use selectFirst if "any one" is okay..pageSize(n) or iterator() and process incrementally..prefetch(...).Expression.fromString(...) or ExpressionFactory.matchExp("fieldName", ...) for static queries. Prefer typed Property constants (Artist.ARTIST_NAME.eq(...)) — they catch typos at compile time and survive model refactors.ObjectContext.commitChanges() is required for changes to persist.