Skip to content

Commit 0954d92

Browse files
committed
docs: update multi-datasource docs for @CompileStatic injection, MultiTenant 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>
1 parent a10d1a7 commit 0954d92

File tree

5 files changed

+162
-6
lines changed

5 files changed

+162
-6
lines changed

grails-data-hibernate5/docs/src/docs/asciidoc/multipleDataSources/dataSourceNamespaces.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,4 @@ def results = c.list {
6262
}
6363
----
6464

65-
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')` instead, which returns a `GormStaticApi` handle with the same methods routed to the specified datasource. See the <<dataServicesMultipleDataSources,Data Services and Multiple Datasources>> section for examples.
65+
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.

grails-data-hibernate5/docs/src/docs/asciidoc/multipleDataSources/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ include::dataSourceNamespaces.adoc[]
3333

3434
=== Using Data Services with Multiple Datasources
3535

36-
When using GORM Data Services (`@Service` annotation) with secondary datasources, you must annotate the abstract class with `@Transactional(connection = 'connectionName')` to route all auto-implemented methods to the correct datasource. See the <<dataServicesMultipleDataSources,Data Services and Multiple Datasources>> section for the full pattern, including `GormEnhancer.findStaticApi()` for complex queries.
36+
GORM Data Services support multi-datasource routing via `@Transactional(connection = 'connectionName')` on the abstract class. All auto-implemented methods - `get()`, `save()`, `delete()`, `findBy*()`, `countBy*()` - route to the specified datasource automatically. This works with `@CompileStatic`, injected `@Service` properties, and `MultiTenant` domain classes. See the <<dataServicesMultipleDataSources,Data Services and Multiple Datasources>> section for the full pattern, including `GormEnhancer.findStaticApi()` for complex queries.
3737

3838
[[connectionSources]]
3939
=== The ConnectionSources API

grails-data-hibernate5/docs/src/docs/asciidoc/services/multipleDataSources.adoc

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ abstract class BookService implements BookDataService {
7373

7474
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.
7575

76-
IMPORTANT: The `connection` parameter is required when using Data Services with secondary datasources. The `@Service(Book)` annotation alone does not determine which datasource to use, even if the `Book` domain class declares `datasource 'books'` in its mapping block.
76+
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.
7777

7878
==== How Connection Routing Works
7979

@@ -189,3 +189,80 @@ class LibraryService {
189189
----
190190

191191
The consuming service does not need `@Transactional(connection = 'books')`. The Data Service handles datasource routing internally.
192+
193+
Data Services can also inject other Data Services. `@CompileStatic` works on `@Service` abstract classes that declare `@Service`-typed properties:
194+
195+
[source,groovy]
196+
----
197+
import grails.gorm.services.Service
198+
199+
interface AuthorDataService {
200+
201+
Author get(Serializable id)
202+
203+
Author save(Author author)
204+
}
205+
----
206+
207+
[source,groovy]
208+
----
209+
import groovy.transform.CompileStatic
210+
import grails.gorm.services.Service
211+
import grails.gorm.transactions.Transactional
212+
213+
@CompileStatic
214+
@Service(Author)
215+
@Transactional(connection = 'books')
216+
abstract class AuthorService implements AuthorDataService {
217+
218+
BookDataService bookDataService // injected @Service property
219+
220+
Map getAuthorWithBooks(Serializable authorId) {
221+
Author author = get(authorId)
222+
List<Book> books = bookDataService.findAllByAuthor(author.name)
223+
[author: author, books: books]
224+
}
225+
}
226+
----
227+
228+
When the Spring context initializes the generated implementation class, it eagerly populates all `@Service`-typed properties via `datastore.getService()`. By the time any user code runs, injected Data Services are fully available.
229+
230+
==== Multi-Tenancy with Multiple Datasources
231+
232+
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:
233+
234+
[source,groovy]
235+
----
236+
import grails.gorm.MultiTenant
237+
238+
class Metric implements MultiTenant<Metric> {
239+
240+
String name
241+
BigDecimal value
242+
243+
static mapping = {
244+
datasource 'analytics'
245+
}
246+
}
247+
----
248+
249+
[source,groovy]
250+
----
251+
import grails.gorm.services.Service
252+
import grails.gorm.transactions.Transactional
253+
import groovy.transform.CompileStatic
254+
255+
@CompileStatic
256+
@Service(Metric)
257+
@Transactional(connection = 'analytics')
258+
abstract class MetricService {
259+
260+
abstract Metric get(Serializable id)
261+
262+
abstract Metric save(Metric metric)
263+
264+
abstract List<Metric> list()
265+
}
266+
----
267+
268+
The `connection` parameter on the abstract class routes all auto-implemented operations - including `save()`, `get()`, and `delete()` - to the `analytics` datasource, regardless of multi-tenancy mode (DATABASE or DISCRIMINATOR).

grails-doc/src/en/guide/conf/dataSource/multipleDatasources.adoc

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ If you have a `Foo` domain class in `dataSource1` and a `Bar` domain class in `d
264264
==== GORM Data Services and Multiple Datasources
265265

266266

267-
GORM Data Services provide a statically compiled alternative to the `static datasource` property shown above. When a `@Service` abstract class needs to route queries to a non-default datasource, use the `connection` parameter of `@Transactional`.
267+
GORM Data Services provide a type-safe, statically compiled approach to multi-datasource access. When a `@Service` abstract class needs to route operations to a non-default datasource, declare the target connection with `@Transactional(connection = 'name')`.
268268

269269

270270
===== Basic Pattern
@@ -307,7 +307,7 @@ abstract class BookService implements BookDataService {
307307

308308
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.
309309

310-
IMPORTANT: The `connection` parameter is required when using Data Services with secondary datasources. The `@Service(Book)` annotation alone does not determine which datasource to use, even if the `Book` domain class has `datasource 'books'` in its mapping block.
310+
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.
311311

312312

313313
===== How Connection Routing Works
@@ -436,6 +436,85 @@ class LibraryService {
436436

437437
The consuming service does not need `@Transactional(connection = 'books')`. The Data Service handles datasource routing internally. If the consuming service coordinates writes across multiple Data Services, annotate the orchestrating method with `@Transactional` to ensure atomicity on the default datasource.
438438

439+
Data Services can also inject other Data Services. `@CompileStatic` works on `@Service` abstract classes that declare `@Service`-typed properties:
440+
441+
[source,groovy]
442+
----
443+
import grails.gorm.services.Service
444+
445+
interface AuthorDataService {
446+
447+
Author get(Serializable id)
448+
449+
Author save(Author author)
450+
}
451+
----
452+
453+
[source,groovy]
454+
----
455+
import groovy.transform.CompileStatic
456+
import grails.gorm.services.Service
457+
import grails.gorm.transactions.Transactional
458+
459+
@CompileStatic
460+
@Service(Author)
461+
@Transactional(connection = 'books')
462+
abstract class AuthorService implements AuthorDataService {
463+
464+
BookDataService bookDataService // injected @Service property
465+
466+
Map getAuthorWithBooks(Serializable authorId) {
467+
Author author = get(authorId)
468+
List<Book> books = bookDataService.findAllByAuthor(author.name)
469+
[author: author, books: books]
470+
}
471+
}
472+
----
473+
474+
When the Spring context initializes the generated implementation class, it eagerly populates all `@Service`-typed properties via `datastore.getService()`. By the time any user code runs, injected Data Services are fully available.
475+
476+
477+
===== Multi-Tenancy with Multiple Datasources
478+
479+
480+
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:
481+
482+
[source,groovy]
483+
----
484+
import grails.gorm.MultiTenant
485+
486+
class Metric implements MultiTenant<Metric> {
487+
488+
String name
489+
BigDecimal value
490+
491+
static mapping = {
492+
datasource 'analytics'
493+
}
494+
}
495+
----
496+
497+
[source,groovy]
498+
----
499+
import grails.gorm.services.Service
500+
import grails.gorm.transactions.Transactional
501+
import groovy.transform.CompileStatic
502+
503+
@CompileStatic
504+
@Service(Metric)
505+
@Transactional(connection = 'analytics')
506+
abstract class MetricService {
507+
508+
abstract Metric get(Serializable id)
509+
510+
abstract Metric save(Metric metric)
511+
512+
abstract List<Metric> list()
513+
}
514+
----
515+
516+
The `connection` parameter on the abstract class routes all auto-implemented operations - including `save()`, `get()`, and `delete()` - to the `analytics` datasource, regardless of multi-tenancy mode (DATABASE or DISCRIMINATOR).
517+
439518

440519
==== Transactions across multiple data sources
441520

grails-doc/src/en/guide/services/declarativeTransactions/transactionsMultiDataSource.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class BookService {
6262
}
6363
----
6464

65-
TIP: When using GORM Data Services (`@Service` annotation), use `@Transactional(connection = 'books')` on the abstract class instead of per-method annotations. This routes all auto-implemented methods to the correct datasource automatically. See the <<multipleDatasources,Data Services and Multiple Datasources>> section for details.
65+
TIP: GORM Data Services offer a simpler approach. Annotate the `@Service` abstract class with `@Transactional(connection = 'books')` and all auto-implemented methods - `get()`, `save()`, `delete()`, `findBy*()`, `countBy*()` - route to the correct datasource automatically. See the <<multipleDatasources,Data Services and Multiple Datasources>> section for the full pattern.
6666

6767
[source, groovy]
6868
----

0 commit comments

Comments
 (0)