Migrating existing Chenile Query applications to the newer provider and pagination model
Edit me

Query Framework Migration Guide

This guide is for applications already using the Chenile query framework with MyBatis mapper XML files and query definition JSON files.

Migration Summary

For existing MyBatis/JDBC applications, migration is intentionally backward compatible.

Most applications do not need to change their existing query XML, query JSON, controller usage, request payloads, datasource configuration, or count queries.

The default behavior remains:

  • Query execution uses MyBatis.
  • Paginated queries execute <queryId>-count.
  • Responses include exact maxRows and maxPages.
  • Existing query.datasources, query.mapperFiles, query.definitionFiles, and query.defaultTenantId properties continue to work.

Existing MyBatis Applications

Keep the existing configuration shape:

query:
  defaultTenantId: tenant1
  mapperFiles: classpath*:query/mapper/*.xml
  definitionFiles: classpath*:query/mapper/*.json
  datasources:
    tenant1:
      type: com.zaxxer.hikari.HikariDataSource
      jdbcUrl: jdbc:h2:mem:demo_tenant1
      username: demo
      password: password

Do not set query.provider for normal MyBatis/JDBC usage. The framework defaults it to:

query:
  provider: mybatis

You also do not need to set query.mybatis.enabled. It defaults to true.

Count Query Behavior

Old behavior remains the default. If a query is marked as paginated, the framework runs:

<queryId>-count

and then runs the main query with pagination.

Example:

{
  "id": "Student.getAll",
  "name": "students",
  "paginated": true
}

The framework expects this mapper id to exist:

Student.getAll-count

No migration is required if this behavior is acceptable.

Optional No-Count Mode

For high-volume list APIs where the count query is expensive, disable count query execution globally:

query:
  pagination:
    countQueryEnabled: false

When disabled, the framework fetches pageSize + 1 rows, trims the extra row, and uses that extra row to determine whether a next page exists.

Response behavior changes in no-count mode:

{
  "maxRows": 0,
  "maxPages": 0,
  "pagination": {
    "countQueryExecuted": false,
    "totalCountAvailable": false,
    "nextPageAvailable": true
  }
}

Use pagination.nextPageAvailable instead of maxPages when count query is disabled.

Response Contract Change

SearchResponse now has an additional nullable field:

private SearchPaginationInfo pagination;

Existing clients can ignore this field. It is mainly useful when query.pagination.countQueryEnabled=false.

When count query is enabled, pagination can remain null and existing maxRows / maxPages behavior is preserved.

Count Result Type

The count query result can now be any Number, not only Integer.

This makes existing count mappers more tolerant of JDBC drivers that return Long for count(*).

No mapper change is required unless your count query returns a non-numeric value.

Provider Extension Migration

The old query framework was tightly coupled to MyBatis execution. The new framework keeps MyBatis as the default but allows applications to provide their own execution provider.

For existing MyBatis/JDBC services, do nothing:

query:
  provider: mybatis

or omit query.provider completely.

For a custom backend, register a Spring bean that implements:

org.chenile.query.service.impl.QueryExecutionProvider

Then select it:

query:
  provider: document

For a provider that does not use MyBatis at all, disable MyBatis infrastructure:

query:
  provider: document
  mybatis:
    enabled: false
  definitionFiles: classpath*:query/definitions/*.json

The query definition JSON is still used for:

  • query lookup by external query name
  • filter enrichment
  • pagination flag
  • column metadata
  • ACL metadata
  • response metadata

The custom provider owns only backend execution, sorting, pagination, and count behavior.

See Query Provider Extension for complete provider examples.

Suggested Migration Steps

  1. Upgrade the query framework dependency.
  2. Keep existing MyBatis mapper XML and query definition JSON unchanged.
  3. Keep existing datasource YAML unchanged.
  4. Run existing query Cucumber/API tests.
  5. Add one test for default pagination to confirm maxRows and maxPages still match old behavior.
  6. If count queries are expensive, add a separate profile with query.pagination.countQueryEnabled=false.
  7. In no-count mode, update API clients to use pagination.nextPageAvailable.
  8. Only create a custom QueryExecutionProvider if the backend cannot be handled by MyBatis/JDBC configuration.

Quick Compatibility Checklist

  • Existing /q/{queryName} REST endpoint: compatible.
  • Existing SearchRequest: compatible.
  • Existing SearchResponse: compatible; one nullable field was added.
  • Existing MyBatis mapper XML: compatible.
  • Existing query definition JSON: compatible.
  • Existing count query mapper ids: compatible.
  • Existing datasource YAML: compatible.
  • Existing multi-tenant routing: compatible.
  • New provider extension: optional.
  • New no-count pagination mode: optional.