diff --git a/jpa/envers/README.adoc b/jpa/envers/README.adoc new file mode 100644 index 00000000..96172a1e --- /dev/null +++ b/jpa/envers/README.adoc @@ -0,0 +1,8 @@ +# Spring Data JPA - Spring Data Envers + +This project contains an example of how to use Spring Data JPA in combination with Spring Data Envers. + +Envers module is a Hibernate module that works both with Hibernate and JPA. It aims to provide an easy auditing / versioning solution for entity classes. + +Spring Data Envers integrates it with Spring Data JPA and makes revision information available directly from JPA based repositories. + diff --git a/jpa/envers/pom.xml b/jpa/envers/pom.xml new file mode 100644 index 00000000..01ae1824 --- /dev/null +++ b/jpa/envers/pom.xml @@ -0,0 +1,20 @@ + + + + spring-data-jpa-examples + org.springframework.data.examples + 2.0.0.BUILD-gh-603-SNAPSHOT + + 4.0.0 + + spring-data-jpa-envers + + + + org.springframework.data + spring-data-envers + + + \ No newline at end of file diff --git a/jpa/envers/src/main/java/example/springdata/jpa/envers/EnversDemoConfiguration.java b/jpa/envers/src/main/java/example/springdata/jpa/envers/EnversDemoConfiguration.java new file mode 100644 index 00000000..d014da30 --- /dev/null +++ b/jpa/envers/src/main/java/example/springdata/jpa/envers/EnversDemoConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.jpa.envers; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.envers.repository.support.EnversRevisionRepositoryFactoryBean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +/** + * Configuration for the Envers Demo. + *

+ * Note that a special {@literal repositoryFactoryBeanClass} is configured for Spring + * Data Envers support. + * + * @author Jens Schauder + */ +@SpringBootApplication +@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class) +public class EnversDemoConfiguration {} diff --git a/jpa/envers/src/main/java/example/springdata/jpa/envers/Person.java b/jpa/envers/src/main/java/example/springdata/jpa/envers/Person.java new file mode 100644 index 00000000..27b372ec --- /dev/null +++ b/jpa/envers/src/main/java/example/springdata/jpa/envers/Person.java @@ -0,0 +1,63 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.jpa.envers; + +import org.hibernate.envers.Audited; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Version; + +/** + * An entity with a single non-technical attribute + * + * @author Jens Schauder + */ +@Entity +@Audited +public class Person { + + @Id @GeneratedValue Long id; + + String name; + + @Version Long version; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } +} diff --git a/jpa/envers/src/main/java/example/springdata/jpa/envers/PersonRepository.java b/jpa/envers/src/main/java/example/springdata/jpa/envers/PersonRepository.java new file mode 100644 index 00000000..0f8b237f --- /dev/null +++ b/jpa/envers/src/main/java/example/springdata/jpa/envers/PersonRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.jpa.envers; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.history.RevisionRepository; + +/** + * Repository for persons with revision support + * + * @author Jens Schauder + */ +public interface PersonRepository extends CrudRepository, RevisionRepository { +} diff --git a/jpa/envers/src/test/java/example/springdata/jpa/envers/EnversIntegrationTests.java b/jpa/envers/src/test/java/example/springdata/jpa/envers/EnversIntegrationTests.java new file mode 100644 index 00000000..5ac13268 --- /dev/null +++ b/jpa/envers/src/test/java/example/springdata/jpa/envers/EnversIntegrationTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.jpa.envers; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.history.Revision; +import org.springframework.data.history.RevisionMetadata.RevisionType; +import org.springframework.data.history.Revisions; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.support.TransactionTemplate; + +import java.util.Iterator; + +/** + * Demonstrating the usage of Spring Data Envers + * + * @author Jens Schauder + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest +public class EnversIntegrationTests { + + @Autowired PersonRepository repository; + @Autowired TransactionTemplate tx; + + @Test + void testRepository() { + + Person updated = preparePersonHistory(); + + Revisions revisions = repository.findRevisions(updated.id); + + + Iterator> revisionIterator = revisions.iterator(); + + checkNextRevision(revisionIterator, "John", RevisionType.INSERT); + checkNextRevision(revisionIterator, "Jonny", RevisionType.UPDATE); + checkNextRevision(revisionIterator, null, RevisionType.DELETE); + assertThat(revisionIterator.hasNext()).isFalse(); + + } + + private void checkNextRevision(Iterator> revisionIterator, String name, RevisionType revisionType) { + + assertThat(revisionIterator.hasNext()).isTrue(); + Revision revision = revisionIterator.next(); + assertThat(revision.getEntity().name).isEqualTo(name); + assertThat(revision.getMetadata().getRevisionType()).isEqualTo(revisionType); + } + + private Person preparePersonHistory() { + + Person john = new Person(); + john.setName("John"); + + //create + Person saved = tx.execute(__ -> repository.save(john)); + 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; + } +} diff --git a/jpa/pom.xml b/jpa/pom.xml index c3124e4b..105943c4 100644 --- a/jpa/pom.xml +++ b/jpa/pom.xml @@ -27,6 +27,7 @@ query-by-example vavr deferred + envers