Skip to content

docs: Add Data Services and GormEnhancer documentation for multi-datasource routing#15406

Draft
jamesfredley wants to merge 3 commits intoapache:7.0.xfrom
jamesfredley:docs/multi-datasource-data-services
Draft

docs: Add Data Services and GormEnhancer documentation for multi-datasource routing#15406
jamesfredley wants to merge 3 commits intoapache:7.0.xfrom
jamesfredley:docs/multi-datasource-data-services

Conversation

@jamesfredley
Copy link
Contributor

@jamesfredley jamesfredley commented Feb 17, 2026

Summary

Adds documentation for using GORM Data Services with multiple datasources - a topic completely absent from both grails-doc and the GORM Hibernate5 docs today. Covers @Transactional(connection) routing, GormEnhancer.findStaticApi() for statically compiled escape-hatch queries, the interface + abstract class pattern, @CompileStatic with injected @Service properties, and MultiTenant entity routing.

This documentation assumes the following fix PRs are merged:

Changes

grails-doc (Grails User Guide)

multipleDatasources.adoc - New "GORM Data Services and Multiple Datasources" section with 6 subsections:

  • Basic Pattern - Interface + abstract class with @Transactional(connection = 'books'), explaining that the connection parameter is required (the @Service annotation alone does not determine datasource routing)
  • How Connection Routing Works - How ServiceTransformation copies the @Transactional(connection) annotation to the generated implementation and how findConnectionId() propagates it to auto-implemented methods
  • Complex Queries with GormEnhancer - GormEnhancer.findStaticApi(Book, 'books') pattern for HQL, criteria, and aggregation queries that cannot be auto-implemented. Includes a reference table of all available GormStaticApi methods
  • Consuming Multi-Datasource Data Services - Injecting the interface type in other services, plus @CompileStatic on @Service abstract classes that inject other @Service-typed properties (enabled by fix: @CompileStatic on @Service abstract class with injected @Service properties fails to compile #15396)
  • Multi-Tenancy with Multiple Datasources - MultiTenant entities with explicit non-default datasource declarations route correctly through Data Services in both DATABASE and DISCRIMINATOR modes (enabled by fix: GormEnhancer.allQualifiers() overrides explicit datasource declarations for MultiTenant entities #15393)

transactionsMultiDataSource.adoc - Added a TIP cross-referencing the new Data Services section, explicitly listing all routed CRUD methods

grails-data-hibernate5 (GORM Hibernate Docs)

New file: services/multipleDataSources.adoc - Full Data Services multi-datasource documentation mirroring the grails-doc content, including:

  • Routing to a secondary datasource with @Transactional(connection)
  • How connection routing works (ServiceTransformation AST chain)
  • Complex queries with GormEnhancer.findStaticApi() and GormStaticApi method reference table
  • Consuming multi-datasource Data Services with @CompileStatic + injected @Service example
  • Multi-Tenancy with explicit datasource and MultiTenant trait

services/index.adoc - Added include for the new section with [[dataServicesMultipleDataSources]] anchor

multipleDataSources/index.adoc - Added "Using Data Services with Multiple Datasources" cross-reference section, noting support for @CompileStatic, injected @Service properties, and MultiTenant domain classes

multipleDataSources/dataSourceNamespaces.adoc - Added TIP noting GormEnhancer.findStaticApi() and Data Services as @CompileStatic-friendly alternatives to namespace syntax

Why This Is Needed

Currently there is zero documentation connecting GORM Data Services to multi-datasource usage:

  • grails-doc multipleDatasources.adoc only shows the old static datasource = 'lookup' pattern
  • GORM Hibernate5 services/ docs cover Data Services in detail but never mention multi-datasource
  • GORM Hibernate5 multipleDataSources/ docs cover namespace syntax but never mention Data Services
  • No documentation mentions @Transactional(connection = 'xxx') for routing
  • No documentation mentions GormEnhancer.findStaticApi() as a public API
  • No documentation covers @CompileStatic on @Service classes with injected @Service properties
  • No documentation covers MultiTenant entities with explicit datasource declarations

These are all essential for production multi-datasource applications using modern GORM patterns. Without this documentation, developers must read the ServiceTransformation source code to understand how connection routing works.

Verified Against Source

All technical claims were verified against grails-core source:

Files Changed

File Change
grails-doc/.../multipleDatasources.adoc New "GORM Data Services and Multiple Datasources" section with @CompileStatic injection + MultiTenant subsections
grails-doc/.../transactionsMultiDataSource.adoc TIP cross-reference listing all routed CRUD methods
grails-data-hibernate5/.../services/multipleDataSources.adoc New file - full multi-datasource Data Services section with @CompileStatic + MultiTenant
grails-data-hibernate5/.../services/index.adoc Include for new section
grails-data-hibernate5/.../multipleDataSources/index.adoc Cross-reference to Data Services section
grails-data-hibernate5/.../multipleDataSources/dataSourceNamespaces.adoc TIP about GormEnhancer and Data Services

Environment Information

  • Grails 7.0.x branch
  • GORM 7.x
  • Groovy 4.0.x

@jamesfredley jamesfredley marked this pull request as ready for review February 19, 2026 01:00
Copilot AI review requested due to automatic review settings February 19, 2026 01:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive documentation for using GORM Data Services with multiple datasources, addressing a significant gap in both the Grails User Guide and GORM Hibernate5 documentation. The changes explain the @Transactional(connection) routing pattern, how ServiceTransformation copies connection annotations, and how to use GormEnhancer.findStaticApi() for complex queries that require HQL, criteria builders, or aggregations.

Changes:

  • Added "GORM Data Services and Multiple Datasources" section to both grails-doc and grails-data-hibernate5 documentation with interface + abstract class pattern examples, connection routing explanations, and GormStaticApi method reference tables
  • Added cross-reference TIPs in transactionsMultiDataSource.adoc and dataSourceNamespaces.adoc directing readers to the new comprehensive Data Services sections
  • Created proper AsciiDoc anchors and cross-references to link the documentation together

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
grails-doc/.../multipleDatasources.adoc Added comprehensive 177-line section documenting Data Services with multiple datasources, including basic pattern, connection routing mechanics, GormEnhancer usage, and method reference table
grails-doc/.../transactionsMultiDataSource.adoc Added TIP cross-referencing the new Data Services section for readers using the @Service annotation
grails-data-hibernate5/.../services/multipleDataSources.adoc New 191-line file mirroring grails-doc content with GORM-specific formatting and cross-references
grails-data-hibernate5/.../services/index.adoc Added include and anchor for the new multipleDataSources section in the Data Services chapter
grails-data-hibernate5/.../multipleDataSources/index.adoc Added cross-reference section directing readers to detailed Data Services documentation
grails-data-hibernate5/.../multipleDataSources/dataSourceNamespaces.adoc Added TIP explaining GormEnhancer.findStaticApi() as @CompileStatic-friendly alternative to namespace syntax

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…iTenant routing, and CRUD connection fixes

Add documentation reflecting the post-fix state after PRs apache#15393,
apache#15395, and apache#15396 are merged:

- Add @CompileStatic + injected @service property example (PR apache#15396)
- Add Multi-Tenancy with explicit datasource section (PR apache#15393)
- List all CRUD methods that respect connection routing (PR apache#15395)
- Soften IMPORTANT boxes to NOTE with authoritative tone

Assisted-by: Claude Code <Claude@Claude.ai>
@jamesfredley jamesfredley self-assigned this Feb 19, 2026
@jamesfredley jamesfredley added this to the grails:7.0.8 milestone Feb 19, 2026
}
----

TIP: The namespace syntax (e.g., `ZipCode.auditing.get(42)`) uses dynamic dispatch and is not compatible with `@CompileStatic`. For statically compiled code, use `GormEnhancer.findStaticApi(ZipCode, 'auditing')` to obtain a `GormStaticApi` handle with the same methods routed to the specified datasource, or use a Data Service with `@Transactional(connection = 'auditing')` for automatic routing. See the <<dataServicesMultipleDataSources,Data Services and Multiple Datasources>> section for the full pattern.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a bug? Recommending people to use GormStaticApi I think is a horrible idea. It's an implementation detail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this back in draft, let's see where we land after fixing the few open bugs and hopefully there is a normal path that is fully unlocked to do this with @CompileStatic


The `@Transactional(connection = 'books')` annotation on the abstract class ensures that all auto-implemented methods (`get`, `save`, `delete`, `findBy*`, `countBy*`, etc.) route to the `books` datasource. Without this annotation, queries silently route to the default datasource.

NOTE: The `@Service(Book)` annotation identifies the domain class but does not determine which datasource to use. Even if `Book` declares `datasource 'books'` in its mapping block, you must specify `@Transactional(connection = 'books')` on the abstract class to route operations to the correct datasource.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like we should open a feature request.


1. Copies the `@Transactional(connection = 'books')` annotation from the abstract class to the generated implementation class
2. For each auto-implemented method, resolves the connection identifier via `findConnectionId()`
3. Generates method bodies that use the appropriate connection - `GormEnhancer.findStaticApi(Book, 'books')` for CRUD operations and `DetachedCriteria.withConnection('books')` for finder queries
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should give this implementation detail. We don't document internal classes as it could cause people to use them thinking they're a public api

}
----

The `GormStaticApi` returned by `findStaticApi()` provides these methods, all routed to the specified datasource:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, we shouldn't be referencing the implementation detail.


1. Copies the `@Transactional(connection = 'books')` annotation from the abstract class to the generated implementation class
2. For each auto-implemented method, resolves the connection identifier via `findConnectionId()`
3. Generates method bodies that use the appropriate connection - `GormEnhancer.findStaticApi(Book, 'books')` for CRUD operations and `DetachedCriteria.withConnection('books')` for finder queries
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same feedback

===== Multi-Tenancy with Multiple Datasources


Domain classes that implement `GormEntity` with the `MultiTenant` trait and declare an explicit non-default datasource (e.g., `datasource 'analytics'`) route correctly through Data Services. GORM's `GormEnhancer` preserves explicit datasource qualifiers for multi-tenant entities, so Data Services using `@Transactional(connection = 'analytics')` work the same way as for non-tenant domain classes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not reference the GormEnhancer in the docs. It's an implementation detail.

@jamesfredley jamesfredley marked this pull request as draft February 19, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants

Comments