Skip to content

Diagnostics

Every failure the library raises carries a stable code of the form MQnnnn, the model's simple name where one applies, and the column or property involved. The meaning of a released code never changes, so you can search for it. Messages say what to change, for example OrderView.totl: no attribute 'totl' on OrderEntity.

Range When How it surfaces
MQ1xxx A query or write definition is invalid, found when build() or the first resolution runs ModelQueryDefinitionException
MQ2xxx Execution, paging, export and bulk writes ModelQueryExecutionException
MQ3xxx Your models, at compile time a compiler error from the annotation processor
MQ4xxx Configuration and vendor resolution, usually at startup ModelQueryConfigurationException

The three exceptions extend ModelQueryException, so one clause catches every failure the library raises, and its code() says which one it was. The one exception is the JPA OptimisticLockException an expectVersion update throws when it affects no rows (see Bulk writes).

try {
    return executor.page(query, PageSpec.of(0, 50), CountMode.NO_COUNT);
} catch (ModelQueryException e) {
    log.warn("query failed with {}", e.code(), e);
    throw e;
}

MQ1xxx: query and write definitions

Code What went wrong
MQ1001 A column's declared type does not match the entity attribute.
MQ1002 An attribute named by a column or join does not exist on its entity.
MQ1003 A column or table sits on a root entity the query is not rooted at.
MQ1101 Two TableFields share a join key but carry different on(...) conditions.
MQ1102 on(...) used without as(...).
MQ1103 Two Agg.of fields share a name with different expressions.
MQ1104 as(...), on(...) or presentBy(...) on a root TableField, which is not a join.
MQ1105 FieldIndex.only names a key the index doesn't hold, or leaves a kept set empty.
MQ1201 keyset() or primaryKeyFirst(...) without a primary key.
MQ1202 build() without select(...) or fetch(...).
MQ1203 Builder given a join instead of a root TableField.
MQ1204 PrimaryKeyFirst.whenOffsetAbove with a negative offset.
MQ1205 QueryCustomizer changed the ORDER BY or GROUP BY of a phase.
MQ1206 A primary-key column of array type.
MQ1207 keyset() with a Float or Double order or primary-key column.
MQ1301 A value-form filter received null.
MQ1302 A column or nested exists(...) path inside exists(...) is not on or below the given path.
MQ1303 A Filters or Having used after its operator returned, or while a nested operator runs.
MQ1304 exists(...) given a root instead of a join path.
MQ1305 A Filters.add predicate returned null.
MQ1306 One in or notIn filter has more values than maxBindParameters().
MQ1307 A statement binds more values than maxBindParameters() together; narrow its filters. A keyset page, export page or write round is refused before it runs when its own binds plus the worst cursor would pass the limit; the message says how many binds each side has.
MQ1308 A value cannot be converted to its column's attribute type, for example an Instant beyond the range of Timestamp; persist setting a primitive attribute to null.
MQ1401 Grouped query: selected non-aggregate column is not in the group-by.
MQ1402 Grouped query: keyset paging or primary-key-first not allowed.
MQ1403 Grouped query: Agg.sum or Agg.sumAsLong over a column whose SQL sum type differs from the result type.
MQ1404 Grouped query: aggregate passed to groupBy, which takes columns only.
MQ1405 Grouped query: Agg.of expression returned null, or Java type does not match declared type.
MQ1406 Grouped query: orderBy key does not fit the grouping (non-group-key column on grouped query, or aggregate on ungrouped one).
MQ1407 Grouped query: having(...) on an ungrouped query.
MQ1408 Grouped query: aggregate function over a column with a ColumnConverter (sum, sumAsLong and avg; min, max and countDistinct take an OrderedColumnField and do not compile over any other converted column).
MQ1409 Grouped query: column selected under a presentBy join whose key columns are not all group keys.
MQ1701 A fetch plan's join plan whose join has no column of the query's selection at or below it.
MQ1702 A column a fetch plan needs (a child's key, an enricher's column) is read through a to-many join; checked on first execution, and count logs a warning instead.
MQ1703 A fetch plan names the same child or join twice.
MQ1704 A fetch plan with a child, at any join depth, on a grouped query.
MQ1705 A join plan selects an aggregate, which cannot be re-rooted under the join.
MQ1601 Bulk write: no predicate left after skipping rows.
MQ1602 Bulk write: column is assigned twice.
MQ1603 Bulk write: set(column, null) called; NULL must be written with setNull.
MQ1604 Bulk write: assigned column is not on the root (self-referencing join).
MQ1605 Bulk write: primary-key or @Version column is assigned.
MQ1606 Bulk write: expectVersion used incorrectly or on a root with wrong type.
MQ1607 Bulk write: Changes.from(...) names a column that is not writable.
MQ1608 Bulk write: @PrimaryKey is not the root entity's id.
MQ1609 Bulk write: setExpression on a column with a converter.
MQ1610 Bulk write: an update throughEntities() combined with keepVersion(), expectVersion(v) or setExpression, whatever the call order.
MQ1611 Write assignment: an unknown path, an id, a @Version, a collection, a to-one or a whole embeddable, overlapping kinds for one path, or an entity class that is a subclass of the write's root.
MQ1612 Write assignment: a type the attribute cannot take, or a supplier returning null or the wrong type.
MQ1801 Insert: a column is not mapped, is mapped or set twice, is not on the written root, or is not in the model's InsertColumns; a map between columns with different converters or types; lockKeys() on insert-values; insertReturningKeys with commitEachChunk().
MQ1802 Insert: the model names a generated id, lacks an id that has no generator, or a row has a null assigned id; persist names a generated id (where the provider reports the generator) or part of a composite id.
MQ1803 Insert: a null row.
MQ1804 Insert conflict clause: the conflict columns are not the id, a natural id or a declared unique constraint; a vendor that detects a conflict on any unique key without anyUniqueKey(); doNothing the provider does not render; a doUpdate assigning a key column or reading two or more assigned columns in its where without conflictUpdateWhereOnAssignedColumns(true).
MQ1805 Insert: a generator that is not supported for the call (a pooled sequence or a table or UUID generator on an insert-select), a JOINED or @SecondaryTable root, a composite id with generated parts, @MapsId, or a constructor-only embeddable under persist.
MQ1806 Insert: a chunked insert-select whose source and target overlap, or whose tables the provider cannot name, or whose source joins through a link or collection table (@ManyToMany, @ElementCollection, a @OneToMany over a join table).
MQ1807 Insert: keys requested for an IDENTITY or assigned id, or a key type that is not the id's type.
MQ1808 Insert: two rows of one insert-values call with a conflict clause share a conflict-key tuple.
MQ1809 persist(persist, returning): a query with a where or having that recorded a filter, groupBy, a fetch plan, customize, orderBy, keyset or primaryKeyFirst, or a column the flushed entity cannot fill.

See Queries and Filters, Grouped queries, Bulk writes and Inserts.

MQ2xxx: execution

Code What went wrong
MQ2001, MQ2002 A page, export or chunk size is not positive, a limit is negative, or an offset is negative.
MQ2101 Streaming needs a transaction on this database (PostgreSQL).
MQ2201 A row's primary key mapped to null during export or primary-key-first paging.
MQ2202 A keyset column is NULL and has no explicit nullsFirst()/nullsLast().
MQ2203 An operation that needs a primary key was used on a query without one.
MQ2204 Key-based paging or offset export over a selection read through a to-many join.
MQ2205 A keyset export page repeated a key of the page before; or a chunked write's key select returned a key it already wrote.
MQ2206 A QueryCustomizer narrows the phases of a primary-key-first query differently.
MQ2301 A sort property resolves to no column or to several, asks for ignoreCase, or names a sort on a query without a primary key.
MQ2501 A bulk write ran without an active transaction.
MQ2502 A chunk of a chunked write failed; ChunkedWriteException says what was committed.
MQ2503 An entity-mode update loaded a proxy your persistence context held for a row, and no ProviderSupport can unwrap it; add model-query-hibernate for Hibernate, or write under CLEAR. Nothing in the chunk was changed.
MQ2601 A to-one @Child found two distinct rows for one key.
MQ2602 An Enricher.of returned null, or a page of another size than it was given.
MQ2603 A parent has more children than maxPerParent, or one statement of a child load read its row cap.
MQ2604 A child row's key equals none of the keys it was matched to: the column's collation is case-insensitive or ignores trailing spaces.
MQ2605 stream with a fetch plan that loads children, join plans or enrichers; use export.

See Paging and export and Bulk writes.

MQ3xxx: your models, at compile time

The processor reports these as compiler errors (a few as warnings) pointing at the field.

Code What went wrong
MQ3001 Unknown attribute, or a dotted @Column(attribute) that leaves embedded values.
MQ3002 Model type is not the entity attribute's type, and no converter provided or built in (Instant or Date over a Timestamp).
MQ3003 @Join attribute is not a to-one association, or the nested model's root does not match the target.
MQ3004 Missing @PrimaryKey on a model with no @Aggregate field.
MQ3005 @Join field is not Optional<X>, or X is not a @QueryModel, or X has an @Aggregate or @Computed field.
MQ3006 Nested model has no @PrimaryKey, so presence cannot be decided.
MQ3007 Join cycle between nested models.
MQ3008 Class model has no no-arg constructor visible from its package.
MQ3009 Record component is primitive and not @PrimaryKey.
MQ3010 Record has only a non-canonical constructor, or is generic.
MQ3011 @FilterColumn path does not resolve, or crosses a collection with no explicit joinType.
MQ3012 Two @FilterColumns with the same alias and path prefix but different joinType.
MQ3013 @FilterColumn name clashes with a generated constant, is reserved, or is not a legal Java name.
MQ3014 converter is not a ColumnConverter between the field type and the attribute type.
MQ3015 Two generated constants would have the same name, or a constant clashes with a reserved one.
MQ3016 A warning, not an error: a column on a to-one association selects the whole entity and has no converter.
MQ3017 The model's root, or the root of a model it nests, is not a class, and no annotation processor generated it. A root another processor generates is fine: the model waits for it.
MQ3018 A @Computed or @Aggregate(expression) class is not a usable ExpressionDefinition<Model, FieldType>: its type arguments are not the model and the field's boxed type, or it has neither a public INSTANCE nor a visible no-arg constructor.
MQ3019 @Computed combined with @PrimaryKey, @Column, @Join, @Child, @Aggregate or @Transient, or on a primitive field.
MQ3020 @Selected field that is not exactly SelectSet<Model>, or a second @Selected field in the model.
MQ3021 @Selected combined with another field annotation of the library.
MQ3022 Warning: a @FilterColumn whose key in fields() a mapped column, a @Computed field or another @FilterColumn already holds is left out of fields() and stays a constant.
MQ3201 Aggregate model: field is primitive.
MQ3202 Aggregate model: field type does not match the function's result type.
MQ3203 Aggregate model: no @GroupBy field and is not singleGroup.
MQ3204 Aggregate model: @GroupBy combined with @Aggregate or @Join.
MQ3205 Aggregate model: @Aggregate(fn = SUM) over a 32-bit attribute.
MQ3206 Aggregate model: @Aggregate(distinct = true) on SUM, AVG, MIN or MAX.
MQ3207 Aggregate model: @QueryModel(singleGroup = true) combined with @GroupBy fields.
MQ3208 Aggregate model: @Aggregate takes attribute or expression, not both.
MQ3301 Update model: field maps through a join or a collection.
MQ3302 Update model: @Join, @Aggregate, @GroupBy, @Computed or @Selected not allowed.
MQ3303 Update model: field maps to the primary key or @Version attribute.
MQ3304 Update model: field maps to an attribute that can't be written.
MQ3305 Update model: to-one attribute written by id with the wrong id type.
MQ3306 Update model: @PrimaryKey is not the root entity's id.
MQ3307 Update model: field generates a change-set member that clashes with Changes<M>.
MQ3401 @Child field is not a List or Optional of a @QueryModel, carries @Join, @Transient, @Aggregate or @GroupBy, is on an update model, or sets both through and foreignKey.
MQ3402 @Child key or foreignKey names no attribute of its root, names an association rather than one of its attributes, or crosses a collection in key. A foreignKey may cross one, for a many-to-many child.
MQ3403 @Child key and foreignKey attributes have different types.
MQ3404 @Child key of several attributes, an embedded value or an array: it takes one attribute each side.
MQ3405 A List @Child without foreignKey (unless it has through), or whose model has no @PrimaryKey; or an Optional @Child whose through crosses a collection, on a model with no @PrimaryKey.
MQ3406 @Child(through) whose path is blank, crosses something other than an association, or ends at another type than the child model's root; whose key is not the parent root's single @Id; or whose child model is grouped.
MQ3501 Insert model: names a generated id, or does not name all of an id that has no generator with @PrimaryKey.
MQ3502 Insert model: @Join, @FilterColumn, @Aggregate, @GroupBy, @Computed, @Child, @Selected or @Transient field.
MQ3503 A type carries more than one of @QueryModel, @UpdateModel and @InsertModel.
MQ3504 Warning: the insert model's root shows the processor no id type, so the generated insert and persist type their keys as Object.

See Models and QModels.

MQ4xxx: configuration

Code What went wrong
MQ4001 modelquery.vendor names an unknown vendor.
MQ4002 Two vendor profiles are registered for the same vendor with no precedence rule.
MQ4003 A property value, or its ModelQueryConfig setting, is out of range.
MQ4004 commitEachChunk() with no ChunkTransactions that serves the write's EntityManagerFactory.
MQ4005 modelquery.vendor set with more than one EntityManagerFactory and no ModelQueryConfigurer.
MQ4006 A ModelQueryConfig bean of your own drops a VendorProfile, WriteAssignment or ChunkTransactions bean, or a set modelquery.* property.
MQ4007 A repository declares ModelQueryRepository of an entity other than its domain type.
MQ4009 A bulk insert on a factory whose persistence provider has no insert support, before any statement. persist still runs.

See Spring Data and the starter and Vendor notes.

Logging

model-query logs through System.Logger, which reaches SLF4J, Logback or Log4j through their System.Logger bridges, and java.util.logging without one (where DEBUG is FINE and TRACE is FINER).

Logger Level What it logs
com.rey.modelquery.core.ModelQuery DEBUG Each query definition build() returns: model, entity, selected fields, primary key, filters (kind and column, each value shown as ?), group-by, order and paging mode.
com.rey.modelquery.jpa.DefaultModelQueryExecutor DEBUG Each list, stream, page, count, export, update and delete call, with its limit, page or export options; a write also lists how it chose its rows (the number of keys or all rows) and its filters, each value shown as ?, and how it runs.
com.rey.modelquery.jpa.DefaultModelQueryExecutor TRACE Each statement's bind count against the vendor's limit, then its rows read or written and the time it took.
DEBUG com.rey.modelquery.core.ModelQuery - built OrderView over OrderEntity: select [id, status, total], primaryKey [id], where [EQ(OrderView.status, ?), GT(OrderView.total, ?)], orderBy [total DESC NULLS LAST], paging keyset
DEBUG com.rey.modelquery.jpa.DefaultModelQueryExecutor - page OrderView: offset 100 size 50, NO_COUNT
DEBUG com.rey.modelquery.jpa.DefaultModelQueryExecutor - update OrderEntity (where [EQ(OrderView.status, ?)]): direct
TRACE com.rey.modelquery.jpa.DefaultModelQueryExecutor - OrderView: statement binds 3 of 65535
TRACE com.rey.modelquery.jpa.DefaultModelQueryExecutor - OrderView: 51 rows in 12 ms

Filter values (the build log shows each as ?), bind values and keyset cursors are never logged, since they often hold personal data, and neither is the statement text. Turn on your provider's SQL and bind logging for those, for example Hibernate's org.hibernate.SQL and org.hibernate.orm.jdbc.bind. With Spring Boot:

logging.level.com.rey.modelquery=DEBUG
logging.level.com.rey.modelquery.jpa.DefaultModelQueryExecutor=TRACE

This works with spring-boot-starter-logging, Spring Boot's default: its jul-to-slf4j bridge carries the records to Logback, and Spring Boot keeps the java.util.logging levels in step with Logback's, at startup and on every later change, such as a Spring Cloud refresh or the loggers endpoint. Through that bridge, a TRACE line prints as DEBUG.

If you exclude spring-boot-starter-logging and add Logback yourself, nothing carries the records to it, and anything below INFO is dropped. Add org.slf4j:slf4j-jdk-platform-logging, which Spring Boot manages: it sends System.Logger straight to SLF4J, keeps TRACE as TRACE, and follows level changes, since SLF4J reads them from Logback each time.

Reporting a problem

If you meet a failure without a code, that is a bug: please report it with the stack trace and the model involved.