Skip to content

Commit 36552dd

Browse files
committed
Document AOT repositories.
See #3830
1 parent 8ec2b60 commit 36552dd

File tree

2 files changed

+201
-0
lines changed

2 files changed

+201
-0
lines changed

src/main/antora/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
** xref:repositories/core-extensions.adoc[]
2626
** xref:repositories/query-keywords-reference.adoc[]
2727
** xref:repositories/query-return-types-reference.adoc[]
28+
** xref:jpa/aot.adoc[]
2829
** xref:jpa/faq.adoc[]
2930
** xref:jpa/glossary.adoc[]
3031
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
= Ahead of Time Optimizations
2+
3+
This chapter covers Spring Data's Ahead of Time (AOT) optimizations that build upon {spring-framework-docs}/core/aot.html[Spring's Ahead of Time Optimizations].
4+
5+
[[aot.bestpractices]]
6+
== Best Practices
7+
8+
=== Annotate your Domain Types
9+
10+
During application startup, Spring scans the classpath for domain classes for early processing of entities.
11+
By annotating your domain types with Spring Data-specific `@Table`, `@Document` or `@Entity` annotations you can aid initial entity scanning and ensure that those types are registered with `ManagedTypes` for Runtime Hints.
12+
Classpath scanning is not possible in native image arrangements and so Spring has to use `ManagedTypes` for the initial entity set.
13+
14+
[[aot.hints]]
15+
== Runtime Hints
16+
17+
Running an application as a native image requires additional information compared to a regular JVM runtime.
18+
Spring Data contributes {spring-framework-docs}/core/aot.html#aot.hints[Runtime Hints] during AOT processing for native image usage.
19+
These are in particular hints for:
20+
21+
* Auditing
22+
* `ManagedTypes` to capture the outcome of class-path scans
23+
* Repositories
24+
** Reflection hints for entities, return types, and Spring Data annotations
25+
** Repository fragments
26+
** Querydsl `Q` classes
27+
** Kotlin Coroutine support
28+
* Web support (Jackson Hints for `PagedModel`)
29+
30+
[[aot.repositories]]
31+
== Ahead of Time Repositories
32+
33+
AOT Repositories are an extension to AOT processing by pre-generating eligible query method implementations.
34+
Query methods are opaque to developers regarding their underlying queries being executed in a query method call.
35+
AOT repositories contribute query method implementations based on derived, annotated, and named queries that are known at build-time.
36+
This optimization moves query method processing from runtime to build-time, which can lead to a significant performance improvement as query methods do not need to be analyzed reflectively upon each application start.
37+
38+
The resulting AOT repository fragment follows the naming scheme of `<Repository FQCN>Impl__Aot` and is placed in the same package as the repository interface.
39+
You can find all queries in their String form for generated repository query methods.
40+
41+
=== Running with AOT Repositories
42+
43+
AOT is a mandatory step to transform a Spring application to a native executable, so it is automatically enabled when running in this mode.
44+
It is also possible to use those optimizations on the JVM by setting the `spring.aot.enabled` and `spring.aot.repositories.enabled` properties to `true`.
45+
46+
AOT repositories contribute configuration changes to the actual repository bean registration to register the generated repository fragment.
47+
48+
NOTE: When AOT optimizations are included, some decisions that have been taken at build-time are hard-coded in the application setup.
49+
For instance, profiles that have been enabled at build-time are automatically enabled at runtime as well.
50+
Also, the Spring Data module implementing a repository is fixed.
51+
Changing the implementation requires AOT re-processing.
52+
53+
=== Eligible Methods
54+
55+
AOT repositories filter methods that are eligible for AOT processing.
56+
These are typically all query methods that are not backed by an xref:repositories/custom-implementations.adoc[implementation fragment].
57+
58+
**Supported Features**
59+
60+
* Derived query methods, `@Query`/`@NativeQuery` and named query methods
61+
* `@Modifying` methods returning `void` or `int`
62+
* `@QueryHints` support
63+
* Pagination, `Slice`, `Stream`, and `Optional` return types
64+
* Sort query rewriting
65+
* DTO Projections
66+
* Value Expressions (Those require a bit of reflective information.
67+
Mind that using Value Expressions requires expression parsing and contextual information to evaluate the expression)
68+
69+
70+
**Limitations**
71+
72+
* Requires Hibernate for AOT processing.
73+
* Configuration of `escapeCharacter` and `queryEnhancerSelector` are not yet considered
74+
* `QueryRewriter` must be a no-args class. `QueryRewriter` beans are not yet supported.
75+
* Methods accepting `ScrollPosition` (e.g. `Keyset` pagination) are not yet supported
76+
77+
**Excluded methods**
78+
79+
* `CrudRepository` and other base interface methods
80+
* Querydsl and Query by Example methods
81+
* Methods whose implementation would be overly complex
82+
** Methods accepting `ScrollPosition (e.g. `Keyset` pagination)
83+
** Stored procedure query methods annotated with `@Procedure`
84+
** For now: Dynamic and interface projections
85+
86+
[[aot.repositories.json]]
87+
== Repository Metadata
88+
89+
AOT processing introspects query methods and collects metadata about repository queries.
90+
Spring Data JPA stores this metadata in JSON files that are named like the repository interface and stored next to it (i.e. within the same package).
91+
Repository JSON Metadata contains details about queries and fragments.
92+
An example for the following repository is shown below:
93+
94+
====
95+
[source,java]
96+
----
97+
interface UserRepository extends CrudRepository<User, Integer> {
98+
99+
List<User> findUserNoArgumentsBy(); <1>
100+
101+
Page<User> findPageOfUsersByLastnameStartingWith(String lastname, Pageable page); <2>
102+
103+
@Query("select u from User u where u.emailAddress = ?1")
104+
User findAnnotatedQueryByEmailAddress(String username); <3>
105+
106+
User findByEmailAddress(String emailAddress); <4>
107+
108+
@Procedure(value = "sp_add")
109+
Integer providedProcedure(@Param("arg") Integer arg); <5>
110+
}
111+
----
112+
113+
<1> Derived query without arguments.
114+
<2> Derived query using pagination.
115+
<3> Annotated query.
116+
<4> Named query.
117+
<5> Stored procedure with a provided procedure name.
118+
While stored procedure methods are included in JSON metadata, their method code blocks are not generated in AOT repositories.
119+
====
120+
121+
[source,json]
122+
----
123+
{
124+
"name": "com.acme.UserRepository",
125+
"module": "",
126+
"type": "IMPERATIVE",
127+
"methods": [
128+
{
129+
"name": "findUserNoArgumentsBy",
130+
"signature": "public abstract java.util.List<com.acme.User> com.acme.UserRepository.findUserNoArgumentsBy()",
131+
"query": {
132+
"query": "SELECT u FROM com.acme.User u"
133+
}
134+
},
135+
{
136+
"name": "findPageOfUsersByLastnameStartingWith",
137+
"signature": "public abstract org.springframework.data.domain.Page<com.acme.User> com.acme.UserRepository.findPageOfUsersByLastnameStartingWith(java.lang.String,org.springframework.data.domain.Pageable)",
138+
"query": {
139+
"query": "SELECT u FROM com.acme.User u WHERE u.lastname LIKE ?1 ESCAPE '\\'",
140+
"count-query": "SELECT COUNT(u) FROM com.acme.User u WHERE u.lastname LIKE ?1 ESCAPE '\\'"
141+
}
142+
},
143+
{
144+
"name": "findAnnotatedQueryByEmailAddress",
145+
"signature": "public abstract com.acme.User com.acme.UserRepository.findAnnotatedQueryByEmailAddress(java.lang.String)",
146+
"query": {
147+
"query": "select u from User u where u.emailAddress = ?1"
148+
}
149+
},
150+
{
151+
"name": "findByEmailAddress",
152+
"signature": "public abstract com.acme.User com.acme.UserRepository.findByEmailAddress(java.lang.String)",
153+
"query": {
154+
"name": "User.findByEmailAddress",
155+
"query": "SELECT u FROM User u WHERE u.emailAddress = ?1"
156+
}
157+
},
158+
{
159+
"name": "providedProcedure",
160+
"signature": "public abstract java.lang.Integer com.acme.UserRepository.providedProcedure(java.lang.Integer)",
161+
"query": {
162+
"procedure": "sp_add"
163+
}
164+
},
165+
{
166+
"name": "count",
167+
"signature": "public abstract long org.springframework.data.repository.CrudRepository.count()",
168+
"fragment": {
169+
"fragment": "org.springframework.data.jpa.repository.support.SimpleJpaRepository"
170+
}
171+
}
172+
]
173+
}
174+
----
175+
176+
Queries may contain the following fields:
177+
178+
* `query`: Query descriptor if the method is a query method.
179+
** `name`: Name of the named query if the query is a named one.
180+
** `query` the query used to obtain the query method result from `EntityManager`
181+
** `count-name`: Name of the named count query if the count query is a named one.
182+
** `count-query`: The count query used to obtain the count for query methods using pagination.
183+
** `procedure-name`: Name of the named stored procedure if the stored procedure is a named one.
184+
** `procedure`: Stored procedure name if the query method uses stored procedures.
185+
* `fragment`: Target fragment if the method call is delegated to a store (repository base class, functional fragment such as Querydsl) or user fragment.
186+
Fragments are either described with just `fragment` if there is no further interface or as `interface` and `fragment` tuple in case there is an interface (such as Querydsl or user-declared fragment interface).
187+
188+
[NOTE]
189+
.Normalized Query Form
190+
====
191+
Static analysis of queries allows only a limited representation of runtime query behavior.
192+
Queries are represented in their normalized (pre-parsed and rewritten) form:
193+
194+
* Value Expressions are replaced with bind markers.
195+
* Queries follow the specified query language (JPQL or native) and do not represent the final SQL query.
196+
Spring Data cannot derive the final SQL queries as this is database-specific and depends on the actual runtime environment and parameters (e.g. Entity Graphs, Lazy Loading).
197+
* Query Metadata does not reflect bind-value processing.
198+
`StartingWith`/`EndingWith` queries prepend/append the wildcard character `%` to the actual bind value.
199+
* Runtime Sort information cannot be incorporated in the query string itself as that detail is not known at build-time.
200+
====

0 commit comments

Comments
 (0)