Skip to content

Commit 847e2e0

Browse files
committed
Delete without result, documentation.
1 parent 6b83dca commit 847e2e0

File tree

4 files changed

+209
-0
lines changed

4 files changed

+209
-0
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/aot/JdbcCodeBlocks.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,20 @@ public CodeBlock build() {
679679
}
680680

681681
private CodeBlock update(Builder builder, Class<?> returnType) {
682+
682683
String result = context.localVariable("result");
683684

685+
if (returnType == Void.TYPE || returnType == Void.class) {
686+
687+
builder.addStatement("getJdbcOperations().update($L, $L)", queryVariableName, parameterSourceVariableName);
688+
689+
if (returnType == Void.class) {
690+
builder.addStatement("return null");
691+
}
692+
693+
return builder.build();
694+
}
695+
684696
builder.addStatement("int $L = getJdbcOperations().update($L, $L)", result, queryVariableName,
685697
parameterSourceVariableName);
686698

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/aot/JdbcRepositoryContributorIntegrationTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,14 @@ void shouldDeleteAnnotated() {
337337
assertThat(fragment.deleteAnnotatedQuery("Walter")).isZero();
338338
}
339339

340+
@Test // GH-2121
341+
void shouldDeleteWithoutResult() {
342+
343+
fragment.deleteWithoutResult("Walter");
344+
345+
assertThat(fragment.findByFirstname("Walter")).isNull();
346+
}
347+
340348
@Test // GH-2121
341349
void shouldDeleteAndReturnByName() {
342350

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/aot/UserRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,8 @@ List<User> findWithParameterNameByFirstnameStartingWithOrFirstnameEndingWith(@Pa
137137
@Query("delete from MY_USER where firstname = :firstname")
138138
int deleteAnnotatedQuery(String firstname);
139139

140+
@Modifying
141+
@Query("delete from MY_USER where firstname = :firstname")
142+
void deleteWithoutResult(String firstname);
143+
140144
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
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+
NOTE: Consider AOT repository classes an internal optimization.
42+
Do not use them directly in your code as generation and implementation details may change in future releases.
43+
44+
=== Running with AOT Repositories
45+
46+
AOT is a mandatory step to transform a Spring application to a native executable, so it is automatically enabled when running in this mode.
47+
When AOT is enabled (either for native compilation or by setting `spring.aot.enabled=true`), AOT repositories are automatically enabled by default.
48+
49+
You can disable AOT repository generation entirely or only disable JDBC AOT repositories:
50+
51+
* Set the `spring.aot.repositories.enabled=false` property to disable generated repositories for all Spring Data modules.
52+
* Set the `spring.aot.jdbc.repositories.enabled=false` property to disable only JDBC AOT repositories.
53+
54+
AOT repositories contribute configuration changes to the actual repository bean registration to register the generated repository fragment.
55+
56+
NOTE: When AOT optimizations are included, some decisions that have been taken at build-time are hard-coded in the application setup.
57+
For instance, profiles that have been enabled at build-time are automatically enabled at runtime as well.
58+
Also, the Spring Data module implementing a repository is fixed.
59+
Changing the implementation requires AOT re-processing.
60+
61+
NOTE: Please provide a `JdbcDialect` to avoid early database access caused by dialect detection.
62+
63+
=== Eligible Methods
64+
65+
AOT repositories filter methods that are eligible for AOT processing.
66+
These are typically all query methods that are not backed by an xref:repositories/custom-implementations.adoc[implementation fragment].
67+
68+
**Supported Features**
69+
70+
* Derived query methods, `@Query` and named query methods
71+
* `@Modifying` methods returning `void`, `int`, and `long`
72+
* Pagination, `Slice`, `Stream`, and `Optional` return types
73+
* DTO and Interface Projections
74+
* Value Expressions
75+
76+
**Limitations**
77+
78+
* Methods accepting `ScrollPosition` (e.g. `Keyset` pagination) are not yet supported
79+
80+
**Excluded methods**
81+
82+
* `CrudRepository`, Querydsl, Query by Example, and other base interface methods as their implementation is provided by the base class respective fragments
83+
* Methods whose implementation would be overly complex
84+
** Methods accepting `ScrollPosition` (e.g. `Keyset` pagination)
85+
86+
[[aot.repositories.json]]
87+
== Repository Metadata
88+
89+
AOT processing introspects query methods and collects metadata about repository queries.
90+
Spring Data JDBC 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 * from User u where username = ?1")
104+
User findAnnotatedQueryByEmailAddress(String username); <3>
105+
106+
User findByEmailAddress(String emailAddress); <4>
107+
}
108+
----
109+
110+
<1> Derived query without arguments.
111+
<2> Derived query using pagination.
112+
<3> Annotated query.
113+
<4> Named query.
114+
While stored procedure methods are included in JSON metadata, their method code blocks are not generated in AOT repositories.
115+
====
116+
117+
[source,json]
118+
----
119+
{
120+
"name": "com.acme.UserRepository",
121+
"module": "JDBC",
122+
"type": "IMPERATIVE",
123+
"methods": [
124+
{
125+
"name": "findUserNoArgumentsBy",
126+
"signature": "public abstract java.util.List<com.acme.User> com.acme.UserRepository.findUserNoArgumentsBy()",
127+
"query": {
128+
"query": "SELECT * FROM User"
129+
}
130+
},
131+
{
132+
"name": "findPageOfUsersByLastnameStartingWith",
133+
"signature": "public abstract org.springframework.data.domain.Page<com.acme.User> com.acme.UserRepository.findPageOfUsersByLastnameStartingWith(java.lang.String,org.springframework.data.domain.Pageable)",
134+
"query": {
135+
"query": "SELECT * FROM User u WHERE lastname LIKE :lastname",
136+
"count-query": "SELECT COUNT(*) FROM User WHERE lastname LIKE :lastname"
137+
}
138+
},
139+
{
140+
"name": "findAnnotatedQueryByEmailAddress",
141+
"signature": "public abstract com.acme.User com.acme.UserRepository.findAnnotatedQueryByEmailAddress(java.lang.String)",
142+
"query": {
143+
"query": "select * from User where emailAddress = ?1"
144+
}
145+
},
146+
{
147+
"name": "findByEmailAddress",
148+
"signature": "public abstract com.acme.User com.acme.UserRepository.findByEmailAddress(java.lang.String)",
149+
"query": {
150+
"name": "User.findByEmailAddress",
151+
"query": "SELECT * FROM User WHERE emailAddress = ?1"
152+
}
153+
},
154+
{
155+
"name": "count",
156+
"signature": "public abstract long org.springframework.data.repository.CrudRepository.count()",
157+
"fragment": {
158+
"fragment": "org.springframework.data.jdbc.repository.support.SimpleJdbcRepository"
159+
}
160+
}
161+
]
162+
}
163+
----
164+
165+
Queries may contain the following fields:
166+
167+
* `query`: Query descriptor if the method is a query method.
168+
** `name`: Name of the named query if the query is a named one.
169+
** `query` the query used to obtain the query method result from `EntityManager`
170+
** `count-name`: Name of the named count query if the count query is a named one.
171+
** `count-query`: The count query used to obtain the count for query methods using pagination.
172+
* `fragment`: Target fragment if the method call is delegated to a store (repository base class, functional fragment such as Querydsl) or user fragment.
173+
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).
174+
175+
[NOTE]
176+
.Normalized Query Form
177+
====
178+
Static analysis of queries allows only a limited representation of runtime query behavior.
179+
Queries are represented in their normalized (pre-parsed and rewritten) form:
180+
181+
* Value Expressions are replaced with bind markers.
182+
* Query Metadata does not reflect bind-value processing.
183+
`StartingWith`/`EndingWith` queries prepend/append the wildcard character `%` to the actual bind value.
184+
* Runtime Sort information cannot be incorporated in the query string itself as that detail is not known at build-time.
185+
====

0 commit comments

Comments
 (0)