Skip to content

Commit 0e74d3e

Browse files
schaudermp911de
authored andcommitted
Adds basic Spring Data Envers specific documentation.
Closes #61 See spring-projects/spring-data-examples#603 Original pull request: #279.
1 parent c8623ec commit 0e74d3e

File tree

3 files changed

+210
-5
lines changed

3 files changed

+210
-5
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@
3535
<email>[email protected]</email>
3636
<organization>Freelancer</organization>
3737
</developer>
38+
<developer>
39+
<name>Jens Schauder</name>
40+
<email>[email protected]</email>
41+
<organization>VMware, Inc.</organization>
42+
<organizationUrl>www.spring.io</organizationUrl>
43+
</developer>
3844
</developers>
3945

4046
<scm>

src/main/asciidoc/envers.adoc

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
[[envers.what]]
2+
== What is Spring Data Envers
3+
4+
Spring Data Envers differs from other Spring Data modules in that it is always used in combination with another Spring Data Module: Spring Data JPA.
5+
It makes typical Envers queries available in repositories for Spring Data JPA.
6+
7+
== What is Envers?
8+
9+
Envers is a Hibernate module which adds auditing capabilities to JPA entities.
10+
This documentation assumes you are familiar with Envers just as Spring Data Envers relies on Envers being properly configured.
11+
12+
[[envers.configuration]]
13+
== Configuration
14+
15+
As a starting point for using Spring Data Envers you need a project with Spring Data JPA on the classpath and an additional `spring-data-envers` dependency.
16+
17+
[source,xml,subs="+attributes"]
18+
----
19+
<dependencies>
20+
21+
<!-- other dependency elements omitted -->
22+
23+
<dependency>
24+
<groupId>org.springframework.data</groupId>
25+
<artifactId>spring-data-envers</artifactId>
26+
<version>{version}</version>
27+
</dependency>
28+
29+
</dependencies>
30+
----
31+
32+
This will also bring `hibernate-envers` into the project as a transient dependency.
33+
34+
In order to enable Spring Data Envers and Spring Data JPA we need to configure two beans and a special `repositoryFactoryBeanClass`
35+
36+
====
37+
[source,java]
38+
----
39+
@Configuration
40+
@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class) // <1>
41+
@EnableTransactionManagement
42+
public class EnversDemoConfiguration {
43+
@Bean
44+
public DataSource dataSource() {
45+
46+
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
47+
return builder.setType(EmbeddedDatabaseType.HSQL).build();
48+
}
49+
50+
@Bean
51+
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
52+
53+
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
54+
vendorAdapter.setGenerateDdl(true);
55+
56+
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
57+
factory.setJpaVendorAdapter(vendorAdapter);
58+
factory.setPackagesToScan("example.springdata.jpa.envers");
59+
factory.setDataSource(dataSource());
60+
return factory;
61+
}
62+
63+
@Bean
64+
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
65+
66+
JpaTransactionManager txManager = new JpaTransactionManager();
67+
txManager.setEntityManagerFactory(entityManagerFactory);
68+
return txManager;
69+
}
70+
}
71+
----
72+
<1> This is the only difference to a normal Spring Data JPA configuration. `EnversRevisionRepositoryFactoryBean` ensures implementations of the methods in `RevisionRepository` are available.
73+
====
74+
75+
In order to actually use Spring Data Envers make one or more repositories into {spring-data-commons-javadoc-base}/org/springframework/data/repository/history/RevisionRepository.html[`RevisionRepository`] by adding it as an extended interface.
76+
77+
====
78+
[source,java]
79+
----
80+
public interface PersonRepository
81+
extends CrudRepository<Person, Long>,
82+
RevisionRepository<Person, Long, Long> // <1>
83+
{}
84+
----
85+
<1> The first type parameter `Person` denotes the entity type, the second (`Long`) the type of the id property and the last one (`Long`) is the type of the revision number.
86+
For Envers in default configuration this should be `Integer` or `Long`.
87+
====
88+
89+
The entity for that repository must be an entity with Envers auditing enabled, i.e. it has an `@Audited` annotation.
90+
91+
[source,java]
92+
----
93+
@Entity
94+
@Audited
95+
public class Person {
96+
97+
@Id @GeneratedValue
98+
Long id;
99+
String name;
100+
@Version Long version;
101+
}
102+
----
103+
104+
[[envers.usage]]
105+
== Usage
106+
107+
You may now use the methods from `RevisionRepository` to query the revisions of the entity as demonstrated in the following test case.
108+
109+
====
110+
[source,java]
111+
----
112+
@ExtendWith(SpringExtension.class)
113+
@Import(EnversDemoConfiguration.class) // <1>
114+
public class EnversIntegrationTests {
115+
116+
final PersonRepository repository;
117+
final TransactionTemplate tx;
118+
119+
EnversIntegrationTests(@Autowired PersonRepository repository, @Autowired PlatformTransactionManager tm) {
120+
this.repository = repository;
121+
this.tx = new TransactionTemplate(tm);
122+
}
123+
124+
@Test
125+
void testRepository() {
126+
127+
Person updated = preparePersonHistory();
128+
129+
Revisions<Long, Person> revisions = repository.findRevisions(updated.id);
130+
131+
Iterator<Revision<Long, Person>> revisionIterator = revisions.iterator();
132+
133+
checkNextRevision(revisionIterator, "John", RevisionType.INSERT);
134+
checkNextRevision(revisionIterator, "Jonny", RevisionType.UPDATE);
135+
checkNextRevision(revisionIterator, null, RevisionType.DELETE);
136+
assertThat(revisionIterator.hasNext()).isFalse();
137+
138+
}
139+
140+
/**
141+
* Checks that the next element in the iterator is a Revision entry referencing a Person
142+
* with the given name after whatever change brought that Revision into existence.
143+
* <p>
144+
* As a side effect the Iterator gets advanced by one element.
145+
*
146+
* @param revisionIterator the iterator to be tested.
147+
* @param name the expected name of the Person referenced by the Revision.
148+
* @param revisionType the type of the revision denoting if it represents an insert, update or delete.
149+
*/
150+
private void checkNextRevision(Iterator<Revision<Long, Person>> revisionIterator, String name,
151+
RevisionType revisionType) {
152+
153+
assertThat(revisionIterator.hasNext()).isTrue();
154+
Revision<Long, Person> revision = revisionIterator.next();
155+
assertThat(revision.getEntity().name).isEqualTo(name);
156+
assertThat(revision.getMetadata().getRevisionType()).isEqualTo(revisionType);
157+
}
158+
159+
/**
160+
* Creates a Person with a couple of changes so it has a non-trivial revision history.
161+
* @return the created Person.
162+
*/
163+
private Person preparePersonHistory() {
164+
165+
Person john = new Person();
166+
john.setName("John");
167+
168+
// create
169+
Person saved = tx.execute(__ -> repository.save(john));
170+
assertThat(saved).isNotNull();
171+
172+
saved.setName("Jonny");
173+
174+
// update
175+
Person updated = tx.execute(__ -> repository.save(saved));
176+
assertThat(updated).isNotNull();
177+
178+
// delete
179+
tx.executeWithoutResult(__ -> repository.delete(updated));
180+
return updated;
181+
}
182+
}
183+
----
184+
<1> This references the application context configuration presented above
185+
====
186+
187+
[[envers.resources]]
188+
== Further Resources
189+
190+
There is a https://github.com/spring-projects/spring-data-examples[Spring Data Envers example in the Spring Data Examples repository] that you can download and play around with to get a feel for how the library works.
191+
192+
You should also check out the https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/history/RevisionRepository.html[Javadoc for `RevisionRepository`] and related classes.
193+
194+
Questions are best asked at https://stackoverflow.com/questions/tagged/spring-data-envers[Stackoverflow using the `spring-data-envers` tag].
195+
196+
The https://github.com/spring-projects/spring-data-envers[source code and issue tracker for Spring Data Envers is hosted at GitHub].
197+
198+

src/main/asciidoc/index.adoc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
= Spring Data Envers - Reference Documentation
2-
Oliver Gierke;
2+
Oliver Gierke; Jens Schauder
33
:revnumber: {version}
44
:revdate: {localdate}
5+
:javadoc-base: https://docs.spring.io/spring-data/envers/docs/{revnumber}/api/
56
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
6-
7+
:spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/commons/docs/current/api/
78
(C) 2008-2021 The original authors.
89

910
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
@@ -14,13 +15,13 @@ include::{spring-data-commons-docs}/dependencies.adoc[leveloffset=+1]
1415
include::{spring-data-commons-docs}/repositories.adoc[leveloffset=+1]
1516

1617
[[reference]]
17-
= Reference Documentation
18+
== Reference Documentation
1819

19-
== TODO
20+
include::envers.adoc[leveloffset=+1]
2021

2122

2223
[[appendix]]
23-
= Appendix
24+
== Appendix
2425

2526
:numbered!:
2627
include::{spring-data-commons-docs}/repository-namespace-reference.adoc[leveloffset=+1]

0 commit comments

Comments
 (0)