Skip to content

Commit cadefa2

Browse files
committed
Restore src/main/asciidoc
This is necessary until the modules that still build using commons adoc are migrated to Antora
1 parent 3374d58 commit cadefa2

22 files changed

+3825
-0
lines changed

src/main/asciidoc/auditing.adoc

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
[[auditing]]
2+
= Auditing
3+
4+
[[auditing.basics]]
5+
== Basics
6+
Spring Data provides sophisticated support to transparently keep track of who created or changed an entity and when the change happened.To benefit from that functionality, you have to equip your entity classes with auditing metadata that can be defined either using annotations or by implementing an interface.
7+
Additionally, auditing has to be enabled either through Annotation configuration or XML configuration to register the required infrastructure components.
8+
Please refer to the store-specific section for configuration samples.
9+
10+
[NOTE]
11+
====
12+
Applications that only track creation and modification dates are not required do make their entities implement <<auditing.auditor-aware, `AuditorAware`>>.
13+
====
14+
15+
[[auditing.annotations]]
16+
=== Annotation-based Auditing Metadata
17+
We provide `@CreatedBy` and `@LastModifiedBy` to capture the user who created or modified the entity as well as `@CreatedDate` and `@LastModifiedDate` to capture when the change happened.
18+
19+
.An audited entity
20+
====
21+
[source,java]
22+
----
23+
class Customer {
24+
25+
@CreatedBy
26+
private User user;
27+
28+
@CreatedDate
29+
private Instant createdDate;
30+
31+
// … further properties omitted
32+
}
33+
----
34+
====
35+
36+
As you can see, the annotations can be applied selectively, depending on which information you want to capture.
37+
The annotations, indicating to capture when changes are made, can be used on properties of type JDK8 date and time types, `long`, `Long`, and legacy Java `Date` and `Calendar`.
38+
39+
Auditing metadata does not necessarily need to live in the root level entity but can be added to an embedded one (depending on the actual store in use), as shown in the snippet below.
40+
41+
.Audit metadata in embedded entity
42+
====
43+
[source,java]
44+
----
45+
class Customer {
46+
47+
private AuditMetadata auditingMetadata;
48+
49+
// … further properties omitted
50+
}
51+
52+
class AuditMetadata {
53+
54+
@CreatedBy
55+
private User user;
56+
57+
@CreatedDate
58+
private Instant createdDate;
59+
60+
}
61+
----
62+
====
63+
64+
[[auditing.interfaces]]
65+
=== Interface-based Auditing Metadata
66+
In case you do not want to use annotations to define auditing metadata, you can let your domain class implement the `Auditable` interface. It exposes setter methods for all of the auditing properties.
67+
68+
[[auditing.auditor-aware]]
69+
=== `AuditorAware`
70+
71+
In case you use either `@CreatedBy` or `@LastModifiedBy`, the auditing infrastructure somehow needs to become aware of the current principal. To do so, we provide an `AuditorAware<T>` SPI interface that you have to implement to tell the infrastructure who the current user or system interacting with the application is. The generic type `T` defines what type the properties annotated with `@CreatedBy` or `@LastModifiedBy` have to be.
72+
73+
The following example shows an implementation of the interface that uses Spring Security's `Authentication` object:
74+
75+
.Implementation of `AuditorAware` based on Spring Security
76+
====
77+
[source, java]
78+
----
79+
class SpringSecurityAuditorAware implements AuditorAware<User> {
80+
81+
@Override
82+
public Optional<User> getCurrentAuditor() {
83+
84+
return Optional.ofNullable(SecurityContextHolder.getContext())
85+
.map(SecurityContext::getAuthentication)
86+
.filter(Authentication::isAuthenticated)
87+
.map(Authentication::getPrincipal)
88+
.map(User.class::cast);
89+
}
90+
}
91+
----
92+
====
93+
94+
The implementation accesses the `Authentication` object provided by Spring Security and looks up the custom `UserDetails` instance that you have created in your `UserDetailsService` implementation. We assume here that you are exposing the domain user through the `UserDetails` implementation but that, based on the `Authentication` found, you could also look it up from anywhere.
95+
96+
[[auditing.reactive-auditor-aware]]
97+
=== `ReactiveAuditorAware`
98+
99+
When using reactive infrastructure you might want to make use of contextual information to provide `@CreatedBy` or `@LastModifiedBy` information.
100+
We provide an `ReactiveAuditorAware<T>` SPI interface that you have to implement to tell the infrastructure who the current user or system interacting with the application is. The generic type `T` defines what type the properties annotated with `@CreatedBy` or `@LastModifiedBy` have to be.
101+
102+
The following example shows an implementation of the interface that uses reactive Spring Security's `Authentication` object:
103+
104+
.Implementation of `ReactiveAuditorAware` based on Spring Security
105+
====
106+
[source, java]
107+
----
108+
class SpringSecurityAuditorAware implements ReactiveAuditorAware<User> {
109+
110+
@Override
111+
public Mono<User> getCurrentAuditor() {
112+
113+
return ReactiveSecurityContextHolder.getContext()
114+
.map(SecurityContext::getAuthentication)
115+
.filter(Authentication::isAuthenticated)
116+
.map(Authentication::getPrincipal)
117+
.map(User.class::cast);
118+
}
119+
}
120+
----
121+
====
122+
123+
The implementation accesses the `Authentication` object provided by Spring Security and looks up the custom `UserDetails` instance that you have created in your `UserDetailsService` implementation. We assume here that you are exposing the domain user through the `UserDetails` implementation but that, based on the `Authentication` found, you could also look it up from anywhere.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
The following example of a Spring `Converter` implementation converts from a `String` to a custom `Email` value object:
2+
3+
[source,java,subs="verbatim,attributes"]
4+
----
5+
@ReadingConverter
6+
public class EmailReadConverter implements Converter<String, Email> {
7+
8+
public Email convert(String source) {
9+
return Email.valueOf(source);
10+
}
11+
}
12+
----
13+
14+
If you write a `Converter` whose source and target type are native types, we cannot determine whether we should consider it as a reading or a writing converter.
15+
Registering the converter instance as both might lead to unwanted results.
16+
For example, a `Converter<String, Long>` is ambiguous, although it probably does not make sense to try to convert all `String` instances into `Long` instances when writing.
17+
To let you force the infrastructure to register a converter for only one way, we provide `@ReadingConverter` and `@WritingConverter` annotations to be used in the converter implementation.
18+
19+
Converters are subject to explicit registration as instances are not picked up from a classpath or container scan to avoid unwanted registration with a conversion service and the side effects resulting from such a registration. Converters are registered with `CustomConversions` as the central facility that allows registration and querying for registered converters based on source- and target type.
20+
21+
`CustomConversions` ships with a pre-defined set of converter registrations:
22+
23+
* JSR-310 Converters for conversion between `java.time`, `java.util.Date` and `String` types.
24+
25+
NOTE: Default converters for local temporal types (e.g. `LocalDateTime` to `java.util.Date`) rely on system-default timezone settings to convert between those types. You can override the default converter, by registering your own converter.
26+
27+
[[customconversions.converter-disambiguation]]
28+
== Converter Disambiguation
29+
30+
Generally, we inspect the `Converter` implementations for the source and target types they convert from and to.
31+
Depending on whether one of those is a type the underlying data access API can handle natively, we register the converter instance as a reading or a writing converter.
32+
The following examples show a writing- and a read converter (note the difference is in the order of the qualifiers on `Converter`):
33+
34+
[source,java]
35+
----
36+
// Write converter as only the target type is one that can be handled natively
37+
class MyConverter implements Converter<Person, String> { … }
38+
39+
// Read converter as only the source type is one that can be handled natively
40+
class MyConverter implements Converter<String, Person> { … }
41+
----

src/main/asciidoc/dependencies.adoc

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
[[dependencies]]
2+
= Dependencies
3+
4+
Due to the different inception dates of individual Spring Data modules, most of them carry different major and minor version numbers. The easiest way to find compatible ones is to rely on the Spring Data Release Train BOM that we ship with the compatible versions defined. In a Maven project, you would declare this dependency in the `<dependencyManagement />` section of your POM as follows:
5+
6+
.Using the Spring Data release train BOM
7+
====
8+
[source, xml, subs="+attributes"]
9+
----
10+
<dependencyManagement>
11+
<dependencies>
12+
<dependency>
13+
<groupId>org.springframework.data</groupId>
14+
<artifactId>spring-data-bom</artifactId>
15+
<version>{releasetrainVersion}</version>
16+
<scope>import</scope>
17+
<type>pom</type>
18+
</dependency>
19+
</dependencies>
20+
</dependencyManagement>
21+
----
22+
====
23+
24+
[[dependencies.train-names]]
25+
[[dependencies.train-version]]
26+
The current release train version is `{releasetrainVersion}`. The train version uses https://calver.org/[calver] with the pattern `YYYY.MINOR.MICRO`.
27+
The version name follows `${calver}` for GA releases and service releases and the following pattern for all other versions: `${calver}-${modifier}`, where `modifier` can be one of the following:
28+
29+
* `SNAPSHOT`: Current snapshots
30+
* `M1`, `M2`, and so on: Milestones
31+
* `RC1`, `RC2`, and so on: Release candidates
32+
33+
You can find a working example of using the BOMs in our https://github.com/spring-projects/spring-data-examples/tree/main/bom[Spring Data examples repository]. With that in place, you can declare the Spring Data modules you would like to use without a version in the `<dependencies />` block, as follows:
34+
35+
.Declaring a dependency to a Spring Data module
36+
====
37+
[source, xml]
38+
----
39+
<dependencies>
40+
<dependency>
41+
<groupId>org.springframework.data</groupId>
42+
<artifactId>spring-data-jpa</artifactId>
43+
</dependency>
44+
<dependencies>
45+
----
46+
====
47+
48+
[[dependencies.spring-boot]]
49+
== Dependency Management with Spring Boot
50+
51+
Spring Boot selects a recent version of the Spring Data modules for you. If you still want to upgrade to a newer version,
52+
set the `spring-data-bom.version` property to the <<dependencies.train-version,train version and iteration>>
53+
you would like to use.
54+
55+
See Spring Boot's https://docs.spring.io/spring-boot/docs/current/reference/html/dependency-versions.html#appendix.dependency-versions.properties[documentation]
56+
(search for "Spring Data Bom") for more details.
57+
58+
[[dependencies.spring-framework]]
59+
== Spring Framework
60+
61+
The current version of Spring Data modules require Spring Framework {springVersion} or better. The modules might also work with an older bugfix version of that minor version. However, using the most recent version within that generation is highly recommended.
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
[[entity-callbacks]]
2+
= Entity Callbacks
3+
4+
The Spring Data infrastructure provides hooks for modifying an entity before and after certain methods are invoked.
5+
Those so called `EntityCallback` instances provide a convenient way to check and potentially modify an entity in a callback fashioned style. +
6+
An `EntityCallback` looks pretty much like a specialized `ApplicationListener`.
7+
Some Spring Data modules publish store specific events (such as `BeforeSaveEvent`) that allow modifying the given entity. In some cases, such as when working with immutable types, these events can cause trouble.
8+
Also, event publishing relies on `ApplicationEventMulticaster`. If configuring that with an asynchronous `TaskExecutor` it can lead to unpredictable outcomes, as event processing can be forked onto a Thread.
9+
10+
Entity callbacks provide integration points with both synchronous and reactive APIs to guarantee in-order execution at well-defined checkpoints within the processing chain, returning a potentially modified entity or an reactive wrapper type.
11+
12+
Entity callbacks are typically separated by API type. This separation means that a synchronous API considers only synchronous entity callbacks and a reactive implementation considers only reactive entity callbacks.
13+
14+
[NOTE]
15+
====
16+
The Entity Callback API has been introduced with Spring Data Commons 2.2. It is the recommended way of applying entity modifications.
17+
Existing store specific `ApplicationEvents` are still published *before* the invoking potentially registered `EntityCallback` instances.
18+
====
19+
20+
[[entity-callbacks.implement]]
21+
== Implementing Entity Callbacks
22+
23+
An `EntityCallback` is directly associated with its domain type through its generic type argument.
24+
Each Spring Data module typically ships with a set of predefined `EntityCallback` interfaces covering the entity lifecycle.
25+
26+
.Anatomy of an `EntityCallback`
27+
====
28+
[source,java]
29+
----
30+
@FunctionalInterface
31+
public interface BeforeSaveCallback<T> extends EntityCallback<T> {
32+
33+
/**
34+
* Entity callback method invoked before a domain object is saved.
35+
* Can return either the same or a modified instance.
36+
*
37+
* @return the domain object to be persisted.
38+
*/
39+
T onBeforeSave(T entity <2>, String collection <3>); <1>
40+
}
41+
----
42+
<1> `BeforeSaveCallback` specific method to be called before an entity is saved. Returns a potentially modifed instance.
43+
<2> The entity right before persisting.
44+
<3> A number of store specific arguments like the _collection_ the entity is persisted to.
45+
====
46+
47+
.Anatomy of a reactive `EntityCallback`
48+
====
49+
[source,java]
50+
----
51+
@FunctionalInterface
52+
public interface ReactiveBeforeSaveCallback<T> extends EntityCallback<T> {
53+
54+
/**
55+
* Entity callback method invoked on subscription, before a domain object is saved.
56+
* The returned Publisher can emit either the same or a modified instance.
57+
*
58+
* @return Publisher emitting the domain object to be persisted.
59+
*/
60+
Publisher<T> onBeforeSave(T entity <2>, String collection <3>); <1>
61+
}
62+
----
63+
<1> `BeforeSaveCallback` specific method to be called on subscription, before an entity is saved. Emits a potentially modifed instance.
64+
<2> The entity right before persisting.
65+
<3> A number of store specific arguments like the _collection_ the entity is persisted to.
66+
====
67+
68+
NOTE: Optional entity callback parameters are defined by the implementing Spring Data module and inferred from call site of `EntityCallback.callback()`.
69+
70+
Implement the interface suiting your application needs like shown in the example below:
71+
72+
.Example `BeforeSaveCallback`
73+
====
74+
[source,java]
75+
----
76+
class DefaultingEntityCallback implements BeforeSaveCallback<Person>, Ordered { <2>
77+
78+
@Override
79+
public Object onBeforeSave(Person entity, String collection) { <1>
80+
81+
if(collection == "user") {
82+
return // ...
83+
}
84+
85+
return // ...
86+
}
87+
88+
@Override
89+
public int getOrder() {
90+
return 100; <2>
91+
}
92+
}
93+
----
94+
<1> Callback implementation according to your requirements.
95+
<2> Potentially order the entity callback if multiple ones for the same domain type exist. Ordering follows lowest precedence.
96+
====
97+
98+
[[entity-callbacks.register]]
99+
== Registering Entity Callbacks
100+
101+
`EntityCallback` beans are picked up by the store specific implementations in case they are registered in the `ApplicationContext`.
102+
Most template APIs already implement `ApplicationContextAware` and therefore have access to the `ApplicationContext`
103+
104+
The following example explains a collection of valid entity callback registrations:
105+
106+
.Example `EntityCallback` Bean registration
107+
====
108+
[source,java]
109+
----
110+
@Order(1) <1>
111+
@Component
112+
class First implements BeforeSaveCallback<Person> {
113+
114+
@Override
115+
public Person onBeforeSave(Person person) {
116+
return // ...
117+
}
118+
}
119+
120+
@Component
121+
class DefaultingEntityCallback implements BeforeSaveCallback<Person>,
122+
Ordered { <2>
123+
124+
@Override
125+
public Object onBeforeSave(Person entity, String collection) {
126+
// ...
127+
}
128+
129+
@Override
130+
public int getOrder() {
131+
return 100; <2>
132+
}
133+
}
134+
135+
@Configuration
136+
public class EntityCallbackConfiguration {
137+
138+
@Bean
139+
BeforeSaveCallback<Person> unorderedLambdaReceiverCallback() { <3>
140+
return (BeforeSaveCallback<Person>) it -> // ...
141+
}
142+
}
143+
144+
@Component
145+
class UserCallbacks implements BeforeConvertCallback<User>,
146+
BeforeSaveCallback<User> { <4>
147+
148+
@Override
149+
public Person onBeforeConvert(User user) {
150+
return // ...
151+
}
152+
153+
@Override
154+
public Person onBeforeSave(User user) {
155+
return // ...
156+
}
157+
}
158+
----
159+
<1> `BeforeSaveCallback` receiving its order from the `@Order` annotation.
160+
<2> `BeforeSaveCallback` receiving its order via the `Ordered` interface implementation.
161+
<3> `BeforeSaveCallback` using a lambda expression. Unordered by default and invoked last. Note that callbacks implemented by a lambda expression do not expose typing information hence invoking these with a non-assignable entity affects the callback throughput. Use a `class` or `enum` to enable type filtering for the callback bean.
162+
<4> Combine multiple entity callback interfaces in a single implementation class.
163+
====

0 commit comments

Comments
 (0)