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