|
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