Skip to content

Commit 1df3cc8

Browse files
Merge latest changes for spring-data-commons
2 parents 6d47797 + 4718c45 commit 1df3cc8

File tree

177 files changed

+2914
-523
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

177 files changed

+2914
-523
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
#Thu Aug 08 10:22:00 CEST 2024
2-
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip
1+
#Thu Nov 07 09:47:21 CET 2024
2+
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

Jenkinsfile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ pipeline {
3737
steps {
3838
script {
3939
docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
40-
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
40+
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
4141
sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' +
42-
"./mvnw -s settings.xml clean dependency:list verify -Dsort -B -U"
42+
"./mvnw -s settings.xml -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-commons -Ddevelocity.storage.directory=/tmp/jenkins-home/.develocity-root clean dependency:list verify -Dsort -B -U"
4343
}
4444
}
4545
}
@@ -67,9 +67,9 @@ pipeline {
6767
steps {
6868
script {
6969
docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
70-
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.basic']) {
70+
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
7171
sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' +
72-
"./mvnw -s settings.xml clean dependency:list verify -Dsort -B"
72+
"./mvnw -s settings.xml -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-commons -Ddevelocity.storage.directory=/tmp/jenkins-home/.develocity-root clean dependency:list verify -Dsort -B"
7373
}
7474
}
7575
}
@@ -99,15 +99,17 @@ pipeline {
9999
steps {
100100
script {
101101
docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
102-
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
102+
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
103103
sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' +
104104
"./mvnw -s settings.xml -Pci,artifactory " +
105+
"-Ddevelocity.storage.directory=/tmp/jenkins-home/.develocity-root " +
105106
"-Dartifactory.server=${p['artifactory.url']} " +
106107
"-Dartifactory.username=${ARTIFACTORY_USR} " +
107108
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
108109
"-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " +
109110
"-Dartifactory.build-name=spring-data-commons " +
110111
"-Dartifactory.build-number=spring-data-commons-${BRANCH_NAME}-build-${BUILD_NUMBER} " +
112+
"-Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-commons " +
111113
"-Dmaven.test.skip=true clean deploy -U -B"
112114
}
113115
}

ci/pipeline.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Java versions
2-
java.main.tag=17.0.12_7-jdk-focal
3-
java.next.tag=22.0.2_9-jdk-jammy
2+
java.main.tag=17.0.13_11-jdk-focal
3+
java.next.tag=23.0.1_11-jdk-noble
44

55
# Docker container images - standard
66
docker.java.main.image=library/eclipse-temurin:${java.main.tag}
@@ -11,6 +11,7 @@ docker.mongodb.4.4.version=4.4.25
1111
docker.mongodb.5.0.version=5.0.21
1212
docker.mongodb.6.0.version=6.0.10
1313
docker.mongodb.7.0.version=7.0.2
14+
docker.mongodb.8.0.version=8.0.0
1415

1516
# Supported versions of Redis
1617
docker.redis.6.version=6.2.13

pom.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
<modelVersion>4.0.0</modelVersion>
55

6-
<groupId>org.springframework.data</groupId>
6+
<groupId>com.github.rodrigorodrigues</groupId>
77
<artifactId>spring-data-commons</artifactId>
8-
<version>3.4.0-SNAPSHOT</version>
8+
<version>3.5.0-SNAPSHOT</version>
99

1010
<name>Spring Data Core</name>
1111
<description>Core Spring concepts underpinning every Spring Data module.</description>
@@ -25,7 +25,7 @@
2525
<parent>
2626
<groupId>org.springframework.data.build</groupId>
2727
<artifactId>spring-data-parent</artifactId>
28-
<version>3.4.0-SNAPSHOT</version>
28+
<version>3.5.0-SNAPSHOT</version>
2929
</parent>
3030

3131
<properties>
@@ -234,7 +234,7 @@
234234
<dependency>
235235
<groupId>org.codehaus.groovy</groupId>
236236
<artifactId>groovy-all</artifactId>
237-
<version>2.4.4</version>
237+
<version>2.4.21</version>
238238
<scope>test</scope>
239239
</dependency>
240240

src/main/antora/modules/ROOT/pages/repositories/core-concepts.adoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44
The central interface in the Spring Data repository abstraction is `Repository`.
55
It takes the domain class to manage as well as the identifier type of the domain class as type arguments.
66
This interface acts primarily as a marker interface to capture the types to work with and to help you to discover interfaces that extend this one.
7+
8+
[TIP]
9+
====
10+
Spring Data considers domain types to be entities, more specifically aggregates.
11+
So you will see the term "entity" used throughout the documentation that can be interchanged with the term "domain type" or "aggregate".
12+
13+
As you might have noticed in the introduction it already hinted towards domain-driven concepts.
14+
We consider domain objects in the sense of DDD.
15+
Domain objects have identifiers (otherwise these would be identity-less value objects), and we somehow need to refer to identifiers when working with certain patterns to access data.
16+
Referring to identifiers will become more meaningful as we talk about repositories and query methods.
17+
====
18+
719
The {spring-data-commons-javadoc-base}/org/springframework/data/repository/CrudRepository.html[`CrudRepository`] and {spring-data-commons-javadoc-base}/org/springframework/data/repository/ListCrudRepository.html[`ListCrudRepository`] interfaces provide sophisticated CRUD functionality for the entity class that is being managed.
820

921
[[repositories.repository]]
@@ -37,6 +49,15 @@ public interface CrudRepository<T, ID> extends Repository<T, ID> {
3749
The methods declared in this interface are commonly referred to as CRUD methods.
3850
`ListCrudRepository` offers equivalent methods, but they return `List` where the `CrudRepository` methods return an `Iterable`.
3951

52+
[IMPORTANT]
53+
====
54+
The repository interface implies a few reserved methods like `findById(ID identifier)` that target the domain type identifier property regardless of its property name.
55+
Read more about this in "`xref:repositories/query-methods-details.adoc#repositories.query-methods.reserved-methods[Defining Query Methods]`".
56+
57+
You can annotate your query method with `@Query` to provide a custom query if a property named `Id` doesn't refer to the identifier.
58+
Following that path can easily lead to confusion and is discouraged as you will quickly hit type limits if the `ID` type and the type of your `Id` property deviate.
59+
====
60+
4061
NOTE: We also provide persistence technology-specific abstractions, such as `JpaRepository` or `MongoRepository`.
4162
Those interfaces extend `CrudRepository` and expose the capabilities of the underlying persistence technology in addition to the rather generic persistence technology-agnostic interfaces such as `CrudRepository`.
4263

src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,32 @@ interface CustomizedUserRepository {
2323
----
2424
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
2525
26+
@Override
2627
public void someCustomMethod(User user) {
2728
// Your custom implementation
2829
}
2930
}
3031
----
3132

32-
NOTE: The most important part of the class name that corresponds to the fragment interface is the `Impl` postfix.
33+
[NOTE]
34+
====
35+
The most important part of the class name that corresponds to the fragment interface is the `Impl` postfix.
36+
You can customize the store-specific postfix by setting `@Enable<StoreModule>Repositories(repositoryImplementationPostfix = …)`.
37+
====
38+
39+
[WARNING]
40+
====
41+
Historically, Spring Data custom repository implementation discovery followed a
42+
https://docs.spring.io/spring-data/commons/docs/1.9.0.RELEASE/reference/html/#repositories.single-repository-behaviour[naming pattern]
43+
that derived the custom implementation class name from the repository allowing effectively a single custom implementation.
44+
45+
A type located in the same package as the repository interface, matching _repository interface name_ followed by _implementation postfix_,
46+
is considered a custom implementation and will be treated as a custom implementation.
47+
A class following that name can lead to undesired behavior.
48+
49+
We consider the single-custom implementation naming deprecated and recommend not using this pattern.
50+
Instead, migrate to a fragment-based programming model.
51+
====
3352

3453
The implementation itself does not depend on Spring Data and can be a regular Spring bean.
3554
Consequently, you can use standard dependency injection behavior to inject references to other beans (such as a `JdbcTemplate`), take part in aspects, and so on.
@@ -63,6 +82,7 @@ interface HumanRepository {
6382
6483
class HumanRepositoryImpl implements HumanRepository {
6584
85+
@Override
6686
public void someHumanMethod(User user) {
6787
// Your custom implementation
6888
}
@@ -77,10 +97,12 @@ interface ContactRepository {
7797
7898
class ContactRepositoryImpl implements ContactRepository {
7999
100+
@Override
80101
public void someContactMethod(User user) {
81102
// Your custom implementation
82103
}
83104
105+
@Override
84106
public User anotherContactMethod(User user) {
85107
// Your custom implementation
86108
}
@@ -115,6 +137,7 @@ interface CustomizedSave<T> {
115137
116138
class CustomizedSaveImpl<T> implements CustomizedSave<T> {
117139
140+
@Override
118141
public <S extends T> S save(S entity) {
119142
// Your custom implementation
120143
}
@@ -271,7 +294,7 @@ package com.acme.search;
271294
272295
import org.springframework.beans.factory.annotation.Autowired;
273296
import org.springframework.data.domain.Limit;
274-
import org.springframework.data.repository.core.support.RepositoryMethodContext;
297+
import org.springframework.data.repository.core.RepositoryMethodContext;
275298
276299
class DefaultSearchExtension<T> implements SearchExtension<T> {
277300
@@ -281,8 +304,9 @@ class DefaultSearchExtension<T> implements SearchExtension<T> {
281304
this.service = service;
282305
}
283306
307+
@Override
284308
public List<T> search(String text, Limit limit) {
285-
return search(RepositoryMethodContext.currentMethod(), text, limit);
309+
return search(RepositoryMethodContext.getContext(), text, limit);
286310
}
287311
288312
List<T> search(RepositoryMethodContext metadata, String text, Limit limit) {
@@ -297,12 +321,12 @@ class DefaultSearchExtension<T> implements SearchExtension<T> {
297321
}
298322
----
299323

300-
In the example above `RepositoryMethodContext.currentMethod()` is used to retrieve metadata for the actual method invocation.
324+
In the example above `RepositoryMethodContext.getContext()` is used to retrieve metadata for the actual method invocation.
301325
`RepositoryMethodContext` exposes information attached to the repository such as the domain type.
302326
In this case we use the repository domain type to identify the name of the index to be searched.
303327

304328
Exposing invocation metadata is costly, hence it is disabled by default.
305-
To access `RepositoryMethodContext.currentMethod()` you need to advise the repository factory responsible for creating the actual repository to expose method metadata.
329+
To access `RepositoryMethodContext.getContext()` you need to advise the repository factory responsible for creating the actual repository to expose method metadata.
306330

307331
.Expose Repository Metadata
308332
[tabs]
@@ -319,7 +343,7 @@ package com.acme.search;
319343
import org.springframework.beans.factory.annotation.Autowired;
320344
import org.springframework.data.domain.Limit;
321345
import org.springframework.data.repository.core.support.RepositoryMetadataAccess;
322-
import org.springframework.data.repository.core.support.RepositoryMethodContext;
346+
import org.springframework.data.repository.core.RepositoryMethodContext;
323347
324348
class DefaultSearchExtension<T> implements SearchExtension<T>, RepositoryMetadataAccess {
325349
@@ -411,6 +435,7 @@ class MyRepositoryImpl<T, ID>
411435
this.entityManager = entityManager;
412436
}
413437
438+
@Override
414439
@Transactional
415440
public <S extends T> S save(S entity) {
416441
// implementation goes here

src/main/antora/modules/ROOT/pages/repositories/definition.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ If you are using a reactive store you might choose `ReactiveCrudRepository`, or
1818

1919
If you are using Kotlin you might pick `CoroutineCrudRepository` which utilizes Kotlin's coroutines.
2020

21-
Additional you can extend `PagingAndSortingRepository`, `ReactiveSortingRepository`, `RxJava3SortingRepository`, or `CoroutineSortingRepository` if you need methods that allow to specify a `Sort` abstraction or in the first case a `Pageable` abstraction.
21+
Additionally you can extend `PagingAndSortingRepository`, `ReactiveSortingRepository`, `RxJava3SortingRepository`, or `CoroutineSortingRepository` if you need methods that allow to specify a `Sort` abstraction or in the first case a `Pageable` abstraction.
2222
Note that the various sorting repositories no longer extended their respective CRUD repository as they did in Spring Data Versions pre 3.0.
2323
Therefore, you need to extend both interfaces if you want functionality of both.
2424

src/main/antora/modules/ROOT/pages/repositories/query-keywords-reference.adoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,22 @@ Consult the store-specific documentation for the exact list of supported keyword
2121
|`…Distinct…`| Use a distinct query to return only unique results. Consult the store-specific documentation whether that feature is supported. This keyword can occur in any place of the subject between `find` (and the other keywords) and `by`.
2222
|===============
2323

24+
[[appendix.query.method.reserved]]
25+
== Reserved methods
26+
27+
The following table lists reserved methods that use predefined functionality (as defined in `CrudRepository`).
28+
These methods are directly invoked on the backing (store-specific) implementation of the repository proxy.
29+
See also "`xref:repositories/query-methods-details.adoc#repositories.query-methods.reserved-methods[Defining Query Methods]`".
30+
31+
.Reserved methods
32+
|===============
33+
|`deleteAllById(Iterable<ID> identifiers)`
34+
|`deleteById(ID identifier)`
35+
|`existsById(ID identifier)`
36+
|`findAllById(Iterable<ID> identifiers)`
37+
|`findById(ID identifier)`
38+
|===============
39+
2440
[[appendix.query.method.predicate]]
2541
== Supported query method predicate keywords and modifiers
2642

src/main/antora/modules/ROOT/pages/repositories/query-methods-details.adoc

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,47 @@ Whether ignoring cases is supported may vary by store, so consult the relevant s
8686
- You can apply static ordering by appending an `OrderBy` clause to the query method that references a property and by providing a sorting direction (`Asc` or `Desc`).
8787
To create a query method that supports dynamic sorting, see "`xref:repositories/query-methods-details.adoc#repositories.special-parameters[Paging, Iterating Large Results, Sorting & Limiting]`".
8888

89+
[[repositories.query-methods.reserved-methods]]
90+
== Reserved Method Names
91+
92+
While derived repository methods bind to properties by name, there are a few exceptions to this rule when it comes to certain method names inherited from the base repository targeting the _identifier_ property.
93+
Those _reserved methods_ like `CrudRepository#findById` (or just `findById`) are targeting the _identifier_ property regardless of the actual property name used in the declared method.
94+
95+
Consider the following domain type holding a property `pk` marked as the identifier via `@Id` and a property called `id`.
96+
In this case you need to pay close attention to the naming of your lookup methods as they may collide with predefined signatures:
97+
98+
====
99+
[source,java]
100+
----
101+
class User {
102+
@Id Long pk; <1>
103+
104+
Long id; <2>
105+
106+
// …
107+
}
108+
109+
interface UserRepository extends Repository<User, Long> {
110+
111+
Optional<User> findById(Long id); <3>
112+
113+
Optional<User> findByPk(Long pk); <4>
114+
115+
Optional<User> findUserById(Long id); <5>
116+
}
117+
----
118+
119+
<1> The identifier property (primary key).
120+
<2> A property named `id`, but not the identifier.
121+
<3> Targets the `pk` property (the one marked with `@Id` which is considered to be the identifier) as it refers to a `CrudRepository` base repository method.
122+
Therefore, it is not a derived query using of `id` as the property name would suggest because it is one of the _reserved methods_.
123+
<4> Targets the `pk` property by name as it is a derived query.
124+
<5> Targets the `id` property by using the descriptive token between `find` and `by` to avoid collisions with _reserved methods_.
125+
====
126+
127+
This special behaviour not only targets lookup methods but also applies to the `exits` and `delete` ones.
128+
Please refer to the "`xref:repositories/query-keywords-reference.adoc#appendix.query.method.reserved[Repository query keywords]`" for the list of methods.
129+
89130
[[repositories.query-methods.query-property-expressions]]
90131
== Property Expressions
91132

@@ -198,7 +239,7 @@ class Products implements Streamable<Product> { <2>
198239
199240
public MonetaryAmount getTotal() { <3>
200241
return streamable.stream()
201-
.map(Priced::getPrice)
242+
.map(Product::getPrice)
202243
.reduce(Money.of(0), MonetaryAmount::add);
203244
}
204245

src/main/antora/modules/ROOT/pages/repositories/scrolling.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ Query execution treats the position parameter _exclusive_, results will start _a
3030
`ScrollPosition#offset()` and `ScrollPosition#keyset()` as special incarnations of a `ScrollPosition` indicating the start of a scroll operation.
3131
====
3232

33+
[NOTE]
34+
====
35+
The above example shows static sorting and limiting.
36+
You can define query methods alternatively that accept a `Sort` object define a more complex sorting order or sorting on a per-request basis.
37+
In a similar way, providing a `Limit` object allows you to define a dynamic limit on a per-request basis instead of applying a static limitation.
38+
Read more on dynamic sorting and limiting in the xref:repositories/query-methods-details.adoc#repositories.special-parameters[Query Methods Details].
39+
====
40+
3341
`WindowIterator` provides a utility to simplify scrolling across ``Window``s by removing the need to check for the presence of a next `Window` and applying the `ScrollPosition`.
3442

3543
[source,java]

0 commit comments

Comments
 (0)