Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>2.5.0-SNAPSHOT</version>
<version>2.5.0.gh-61-SNAPSHOT</version>

<parent>
<groupId>org.springframework.data.build</groupId>
Expand Down Expand Up @@ -35,6 +35,12 @@
<email>[email protected]</email>
<organization>Freelancer</organization>
</developer>
<developer>
<name>Jens Schauder</name>
<email>[email protected]</email>
<organization>VMware, Inc.</organization>
<organizationUrl>www.spring.io</organizationUrl>
</developer>
</developers>

<scm>
Expand Down
134 changes: 134 additions & 0 deletions src/main/asciidoc/envers.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
[[envers.what]]
== What is Spring Data Envers

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.
It makes typical Envers queries available in repositories for Spring Data JPA.

== What is Envers?

Envers is a Hibernate module which adds auditing capabilities to JPA entities.
This documentation assumes you are familiar with Envers just as Spring Data Envers relies on Envers being properly configured.


[[envers.getting-started]]
== Getting Started

As a starting point for using Spring Data Envers you need a project with Spring Data JPA.
The easiest way to create that is using the Spring Initializr either on start.spring.io or via its integration in your favorite IDE.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Link to start.spring.io


Now add spring-data-envers as a dependency
[source,xml,subs="+attributes"]
----
<dependencies>

<!-- other dependency elements omitted -->

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>{version}</version>
</dependency>

</dependencies>
----
This will also bring hibernate-envers into the project as a transient dependency.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Code fences for hibernate-envers


Next is to 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.

[source,java]
----
public interface PersonRepository
extends CrudRepository<Person, Long>,
RevisionRepository<Person, Long, Long> {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add callouts and explain Person, Long, Long. It's not obvious why there's Long, Long.

----

In order to provide an implementations contained in that interface we have to use a different `repositoryFactoryBeanClass` for creating repositories.

[source,java]
----
@SpringBootApplication
@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
public class EnversDemoConfiguration {}
----

The entity for that repository should be an entity with Envers auditing enabled, i.e. it has an `@Audited` annotation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should or is this a hard requirement?


[source,java]
----
@Entity
@Audited
public class Person {

@Id @GeneratedValue
Long id;
String name;
@Version Long version;
}
----

You may now use the methods from `RevisionRepository` to query the revisions of the entity as demonstrated in the following test case.



[source,java]
----
@ExtendWith(SpringExtension.class)
@SpringBootTest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rewrite the test to not require Spring Boot?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't that mean we have to reference an application context configuration which then should also be included in the example? Or are you aiming for something else?

public class EnversIntegrationTests {

@Autowired PersonRepository repository;
@Autowired TransactionTemplate tx;

@Test
void testRepository() {

Person updated = preparePersonHistory();

Revisions<Long, Person> revisions = repository.findRevisions(updated.id);

assertThat(revisions) //
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The // are rather distracting. Also, let's rewrite the test to a more simple form. extracting(…) and tuple aren't quite what we should expect from our audience as pre-requisite.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fully agree on the //
But I consider AssertJ the de facto standard of assertions and extracting + tuple essential and easy to understand features. Rewriting this would make it harder to understand AND enforces bad practices. I'm somewhat reluctant to do that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My motivation is that our example should be focusing on our bits/Envers bits as much as possible. The tests should just decorate the example and require as little knowledge as possible as the cognitive load to understand how to use Revisions is quite high for someone that wants to learn the topic. We should reduce the impact of our decoration on the learning steps.

.extracting( //
rev -> rev.getEntity().name, //
rev -> rev.getMetadata().getRevisionType()) //
.containsExactly( //
tuple("John", RevisionType.INSERT), //
tuple("Jonny", RevisionType.UPDATE), //
tuple(null, RevisionType.DELETE) //
);
}

private Person preparePersonHistory() {

Person john = new Person();
john.setName("John");

//create
Person saved = tx.execute(__ -> repository.save(john));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's replace __ with transactionStatus for a more expressive example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again I don't find that more expressive but unnecessary verbose.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it's more verbose it avoids confusion with Kotlin or Scala-like implicit lambda parameters and it's not implying that any potentially newer and lesser-known feature is used. That being said, trying to remove obstacles.

assertThat(saved).isNotNull();

saved.setName("Jonny");

// update
Person updated = tx.execute(__ -> repository.save(saved));
assertThat(updated).isNotNull();

// delete
tx.executeWithoutResult(__->repository.delete(updated));
return updated;
}
}
----


[[envers.resources]]
== Further Resources

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.

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.

Questions are best asked at https://stackoverflow.com/questions/tagged/spring-data-envers[Stackoverflow using the `spring-data-envers` tag].

The https://github.com/spring-projects/spring-data-envers[source code and issue tracker for Spring Data Envers is hosted at GitHub].


11 changes: 6 additions & 5 deletions src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
= Spring Data Envers - Reference Documentation
Oliver Gierke;
Oliver Gierke; Jens Schauder
:revnumber: {version}
:revdate: {localdate}
:javadoc-base: https://docs.spring.io/spring-data/envers/docs/{revnumber}/api/
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc

:spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/commons/docs/current/api/
(C) 2008-2021 The original authors.

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.
Expand All @@ -14,13 +15,13 @@ include::{spring-data-commons-docs}/dependencies.adoc[leveloffset=+1]
include::{spring-data-commons-docs}/repositories.adoc[leveloffset=+1]

[[reference]]
= Reference Documentation
== Reference Documentation

== TODO
include::envers.adoc[leveloffset=+1]


[[appendix]]
= Appendix
== Appendix

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