diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index f3dbe04e79f..74a370fb324 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -112,6 +112,7 @@ dependencies { optional("org.springframework:spring-webmvc") optional("org.springframework.batch:spring-batch-core") optional("org.springframework.data:spring-data-couchbase") + optional("org.springframework.data:spring-data-envers") optional("org.springframework.data:spring-data-jpa") optional("org.springframework.data:spring-data-rest-webmvc") optional("org.springframework.data:spring-data-cassandra") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/EnversRevisionRepositoriesRegistrar.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/EnversRevisionRepositoriesRegistrar.java new file mode 100644 index 00000000000..e7a96433b6b --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/EnversRevisionRepositoriesRegistrar.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-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 org.springframework.boot.autoconfigure.data.jpa; + +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.data.envers.repository.support.EnversRevisionRepositoryFactoryBean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +/** + * {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data Envers + * Repositories. + * + * @author Stefano Cordio + */ +class EnversRevisionRepositoriesRegistrar extends JpaRepositoriesRegistrar { + + @Override + protected Class getConfiguration() { + return EnableJpaRepositoriesConfiguration.class; + } + + @EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class) + private static class EnableJpaRepositoriesConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java index be992aeee03..1bc95cdbc93 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.JpaRepositoriesImportSelector; import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; @@ -34,11 +35,17 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportSelector; import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.envers.repository.config.EnableEnversRepositories; +import org.springframework.data.envers.repository.support.EnversRevisionRepositoryFactoryBean; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; +import org.springframework.data.repository.history.RevisionRepository; +import org.springframework.util.ClassUtils; /** * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's JPA Repositories. @@ -50,11 +57,17 @@ import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; * Once in effect, the auto-configuration is the equivalent of enabling JPA repositories * using the {@link EnableJpaRepositories @EnableJpaRepositories} annotation. *

+ * In case {@link EnableEnversRepositories} is on the classpath, + * {@link EnversRevisionRepositoryFactoryBean} is used instead of + * {@link JpaRepositoryFactoryBean} to support {@link RevisionRepository} with Hibernate + * Envers. + *

* This configuration class will activate after the Hibernate auto-configuration. * * @author Phillip Webb * @author Josh Long * @author Scott Frederick + * @author Stefano Cordio * @since 1.0.0 * @see EnableJpaRepositories */ @@ -64,7 +77,7 @@ import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; @ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class }) @ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) -@Import(JpaRepositoriesRegistrar.class) +@Import(JpaRepositoriesImportSelector.class) @AutoConfigureAfter({ HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class }) public class JpaRepositoriesAutoConfiguration { @@ -106,4 +119,22 @@ public class JpaRepositoriesAutoConfiguration { } + static class JpaRepositoriesImportSelector implements ImportSelector { + + private static final boolean ENVERS_AVAILABLE = ClassUtils.isPresent( + "org.springframework.data.envers.repository.config.EnableEnversRepositories", + JpaRepositoriesImportSelector.class.getClassLoader()); + + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + return new String[] { determineImport() }; + } + + private String determineImport() { + return ENVERS_AVAILABLE ? EnversRevisionRepositoriesRegistrar.class.getName() + : JpaRepositoriesRegistrar.class.getName(); + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/AbstractJpaRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/AbstractJpaRepositoriesAutoConfigurationTests.java new file mode 100644 index 00000000000..8c529abea53 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/AbstractJpaRepositoriesAutoConfigurationTests.java @@ -0,0 +1,187 @@ +/* + * Copyright 2012-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 org.springframework.boot.autoconfigure.data.jpa; + +import javax.persistence.EntityManagerFactory; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.data.alt.elasticsearch.CityElasticsearchDbRepository; +import org.springframework.boot.autoconfigure.data.alt.jpa.CityJpaRepository; +import org.springframework.boot.autoconfigure.data.alt.mongo.CityMongoDbRepository; +import org.springframework.boot.autoconfigure.data.jpa.city.City; +import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository; +import org.springframework.boot.autoconfigure.data.jpa.country.Country; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.Import; +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.PlatformTransactionManager; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Base class for {@link JpaRepositoriesAutoConfiguration} tests. + * + * @author Dave Syer + * @author Oliver Gierke + * @author Scott Frederick + * @author Stefano Cordio + */ +abstract class AbstractJpaRepositoriesAutoConfigurationTests { + + final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(HibernateJpaAutoConfiguration.class, + JpaRepositoriesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class)) + .withUserConfiguration(EmbeddedDataSourceConfiguration.class); + + @Test + void testDefaultRepositoryConfiguration() { + this.contextRunner.withUserConfiguration(TestConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(CityRepository.class); + assertThat(context).hasSingleBean(PlatformTransactionManager.class); + assertThat(context).hasSingleBean(EntityManagerFactory.class); + assertThat(context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()).isNull(); + }); + } + + @Test + void testOverrideRepositoryConfiguration() { + this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(CityJpaRepository.class); + assertThat(context).hasSingleBean(PlatformTransactionManager.class); + assertThat(context).hasSingleBean(EntityManagerFactory.class); + }); + } + + @Test + void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRepositories() { + this.contextRunner.withUserConfiguration(SortOfInvalidCustomConfiguration.class) + .run((context) -> assertThat(context).doesNotHaveBean(CityRepository.class)); + } + + @Test + void whenBootstrapModeIsLazyWithMultipleAsyncExecutorBootstrapExecutorIsConfigured() { + this.contextRunner.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class) + .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class, + TaskSchedulingAutoConfiguration.class)) + .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy") + .run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()) + .isEqualTo(context.getBean("applicationTaskExecutor"))); + } + + @Test + void whenBootstrapModeIsLazyWithSingleAsyncExecutorBootstrapExecutorIsConfigured() { + this.contextRunner.withUserConfiguration(SingleAsyncTaskExecutorConfiguration.class) + .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy") + .run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()) + .isEqualTo(context.getBean("testAsyncTaskExecutor"))); + } + + @Test + void whenBootstrapModeIsDeferredBootstrapExecutorIsConfigured() { + this.contextRunner.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class) + .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class, + TaskSchedulingAutoConfiguration.class)) + .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=deferred") + .run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()) + .isEqualTo(context.getBean("applicationTaskExecutor"))); + } + + @Test + void whenBootstrapModeIsDefaultBootstrapExecutorIsNotConfigured() { + this.contextRunner.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class) + .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class, + TaskSchedulingAutoConfiguration.class)) + .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=default").run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()).isNull()); + } + + @Test + void bootstrapModeIsDefaultByDefault() { + this.contextRunner.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class) + .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class, + TaskSchedulingAutoConfiguration.class)) + .run((context) -> assertThat( + context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()).isNull()); + } + + @Configuration(proxyBeanMethods = false) + @EnableScheduling + @Import(TestConfiguration.class) + static class MultipleAsyncTaskExecutorConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + @Import(TestConfiguration.class) + static class SingleAsyncTaskExecutorConfiguration { + + @Bean + SimpleAsyncTaskExecutor testAsyncTaskExecutor() { + return new SimpleAsyncTaskExecutor(); + } + + } + + @Configuration(proxyBeanMethods = false) + @TestAutoConfigurationPackage(City.class) + static class TestConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + @EnableJpaRepositories( + basePackageClasses = org.springframework.boot.autoconfigure.data.alt.jpa.CityJpaRepository.class, + excludeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = CityMongoDbRepository.class), + @Filter(type = FilterType.ASSIGNABLE_TYPE, value = CityElasticsearchDbRepository.class) }) + @TestAutoConfigurationPackage(City.class) + static class CustomConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + // To not find any repositories + @EnableJpaRepositories("foo.bar") + @TestAutoConfigurationPackage(City.class) + static class SortOfInvalidCustomConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + @TestAutoConfigurationPackage(Country.class) + static class RevisionRepositoryConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/EnversRevisionRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/EnversRevisionRepositoriesAutoConfigurationTests.java new file mode 100644 index 00000000000..9e965bcac09 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/EnversRevisionRepositoriesAutoConfigurationTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-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 org.springframework.boot.autoconfigure.data.jpa; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.data.jpa.country.CountryRepository; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JpaRepositoriesAutoConfiguration} with Spring Data Envers on the + * classpath. + * + * @author Stefano Cordio + */ +class EnversRevisionRepositoriesAutoConfigurationTests extends AbstractJpaRepositoriesAutoConfigurationTests { + + @Test + void autoConfigurationShouldSucceedWithRevisionRepository() { + this.contextRunner.withUserConfiguration(RevisionRepositoryConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(CountryRepository.class)); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java index c25490bd508..4efde42ec59 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfigurationTests.java @@ -16,164 +16,26 @@ package org.springframework.boot.autoconfigure.data.jpa; -import javax.persistence.EntityManagerFactory; - import org.junit.jupiter.api.Test; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.data.alt.elasticsearch.CityElasticsearchDbRepository; -import org.springframework.boot.autoconfigure.data.alt.jpa.CityJpaRepository; -import org.springframework.boot.autoconfigure.data.alt.mongo.CityMongoDbRepository; -import org.springframework.boot.autoconfigure.data.jpa.city.City; -import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository; -import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; -import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.context.annotation.Import; -import org.springframework.core.task.SimpleAsyncTaskExecutor; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link JpaRepositoriesAutoConfiguration}. + * Tests for {@link JpaRepositoriesAutoConfiguration} without Spring Data Envers on the + * classpath. * - * @author Dave Syer - * @author Oliver Gierke - * @author Scott Frederick + * @author Stefano Cordio */ -class JpaRepositoriesAutoConfigurationTests { - - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(HibernateJpaAutoConfiguration.class, - JpaRepositoriesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class)) - .withUserConfiguration(EmbeddedDataSourceConfiguration.class); +@ClassPathExclusions("spring-data-envers-*.jar") +class JpaRepositoriesAutoConfigurationTests extends AbstractJpaRepositoriesAutoConfigurationTests { @Test - void testDefaultRepositoryConfiguration() { - this.contextRunner.withUserConfiguration(TestConfiguration.class).run((context) -> { - assertThat(context).hasSingleBean(CityRepository.class); - assertThat(context).hasSingleBean(PlatformTransactionManager.class); - assertThat(context).hasSingleBean(EntityManagerFactory.class); - assertThat(context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()).isNull(); - }); - } - - @Test - void testOverrideRepositoryConfiguration() { - this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> { - assertThat(context).hasSingleBean(CityJpaRepository.class); - assertThat(context).hasSingleBean(PlatformTransactionManager.class); - assertThat(context).hasSingleBean(EntityManagerFactory.class); - }); - } - - @Test - void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRepositories() { - this.contextRunner.withUserConfiguration(SortOfInvalidCustomConfiguration.class) - .run((context) -> assertThat(context).doesNotHaveBean(CityRepository.class)); - } - - @Test - void whenBootstrapModeIsLazyWithMultipleAsyncExecutorBootstrapExecutorIsConfigured() { - this.contextRunner.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class) - .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class, - TaskSchedulingAutoConfiguration.class)) - .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy") - .run((context) -> assertThat( - context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()) - .isEqualTo(context.getBean("applicationTaskExecutor"))); - } - - @Test - void whenBootstrapModeIsLazyWithSingleAsyncExecutorBootstrapExecutorIsConfigured() { - this.contextRunner.withUserConfiguration(SingleAsyncTaskExecutorConfiguration.class) - .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=lazy") - .run((context) -> assertThat( - context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()) - .isEqualTo(context.getBean("testAsyncTaskExecutor"))); - } - - @Test - void whenBootstrapModeIsDeferredBootstrapExecutorIsConfigured() { - this.contextRunner.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class) - .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class, - TaskSchedulingAutoConfiguration.class)) - .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=deferred") - .run((context) -> assertThat( - context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()) - .isEqualTo(context.getBean("applicationTaskExecutor"))); - } - - @Test - void whenBootstrapModeIsDefaultBootstrapExecutorIsNotConfigured() { - this.contextRunner.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class) - .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class, - TaskSchedulingAutoConfiguration.class)) - .withPropertyValues("spring.data.jpa.repositories.bootstrap-mode=default").run((context) -> assertThat( - context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()).isNull()); - } - - @Test - void bootstrapModeIsDefaultByDefault() { - this.contextRunner.withUserConfiguration(MultipleAsyncTaskExecutorConfiguration.class) - .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class, - TaskSchedulingAutoConfiguration.class)) - .run((context) -> assertThat( - context.getBean(LocalContainerEntityManagerFactoryBean.class).getBootstrapExecutor()).isNull()); - } - - @Configuration(proxyBeanMethods = false) - @EnableScheduling - @Import(TestConfiguration.class) - static class MultipleAsyncTaskExecutorConfiguration { - - } - - @Configuration(proxyBeanMethods = false) - @Import(TestConfiguration.class) - static class SingleAsyncTaskExecutorConfiguration { - - @Bean - SimpleAsyncTaskExecutor testAsyncTaskExecutor() { - return new SimpleAsyncTaskExecutor(); - } - - } - - @Configuration(proxyBeanMethods = false) - @TestAutoConfigurationPackage(City.class) - static class TestConfiguration { - - } - - @Configuration(proxyBeanMethods = false) - @EnableJpaRepositories( - basePackageClasses = org.springframework.boot.autoconfigure.data.alt.jpa.CityJpaRepository.class, - excludeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = CityMongoDbRepository.class), - @Filter(type = FilterType.ASSIGNABLE_TYPE, value = CityElasticsearchDbRepository.class) }) - @TestAutoConfigurationPackage(City.class) - static class CustomConfiguration { - - } - - @Configuration(proxyBeanMethods = false) - // To not find any repositories - @EnableJpaRepositories("foo.bar") - @TestAutoConfigurationPackage(City.class) - static class SortOfInvalidCustomConfiguration { - + void autoConfigurationShouldFailWithRevisionRepository() { + this.contextRunner.withUserConfiguration(RevisionRepositoryConfiguration.class) + .run((context) -> assertThat(context).getFailure().isInstanceOf(BeanCreationException.class)); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/country/Country.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/country/Country.java new file mode 100644 index 00000000000..878ed9aa132 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/country/Country.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-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 org.springframework.boot.autoconfigure.data.jpa.country; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.envers.Audited; + +@Entity +public class Country implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue + private Long id; + + @Audited + @Column + private String name; + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/country/CountryRepository.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/country/CountryRepository.java new file mode 100644 index 00000000000..8f2034c4b82 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/country/CountryRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-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 org.springframework.boot.autoconfigure.data.jpa.country; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.history.RevisionRepository; + +public interface CountryRepository extends JpaRepository, RevisionRepository { + +} diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index 1110634080e..ebe9085e750 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -100,6 +100,7 @@ dependencies { implementation("org.springframework.data:spring-data-cassandra") implementation("org.springframework.data:spring-data-couchbase") implementation("org.springframework.data:spring-data-elasticsearch") + implementation("org.springframework.data:spring-data-envers") implementation("org.springframework.data:spring-data-jpa") implementation("org.springframework.data:spring-data-ldap") implementation("org.springframework.data:spring-data-mongodb") @@ -226,6 +227,7 @@ tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) { "spring-boot-version": project.version, "spring-data-commons-version": versionConstraints["org.springframework.data:spring-data-commons"], "spring-data-couchbase-version": versionConstraints["org.springframework.data:spring-data-couchbase"], + "spring-data-envers-version": versionConstraints["org.springframework.data:spring-data-envers"], "spring-data-jdbc-version": versionConstraints["org.springframework.data:spring-data-jdbc"], "spring-data-jpa-version": versionConstraints["org.springframework.data:spring-data-jpa"], "spring-data-mongodb-version": versionConstraints["org.springframework.data:spring-data-mongodb"], diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc index 91025fd3094..7957b88c874 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc @@ -60,6 +60,8 @@ :spring-data-couchbase-docs: https://docs.spring.io/spring-data/couchbase/docs/{spring-data-couchbase-version}/reference/html/ :spring-data-elasticsearch: https://spring.io/projects/spring-data-elasticsearch :spring-data-elasticsearch-docs: https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/ +:spring-data-envers: https://spring.io/projects/spring-data-envers +:spring-data-envers-doc: https://docs.spring.io/spring-data/envers/docs/{spring-data-envers-version}/reference/html/ :spring-data-gemfire: https://spring.io/projects/spring-data-gemfire :spring-data-geode: https://spring.io/projects/spring-data-geode :spring-data-jpa: https://spring.io/projects/spring-data-jpa diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/sql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/sql.adoc index bb2aab50340..206fee2ee4e 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/sql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/sql.adoc @@ -247,6 +247,21 @@ For complete details, see the {spring-data-jpa-docs}[Spring Data JPA reference d +[[features.sql.jpa-and-spring-data.envers-repositories]] +==== Spring Data Envers Repositories +If {spring-data-envers}[Spring Data Envers] is available, JPA repositories are auto-configured to support typical Envers queries. + +To use Spring Data Envers, make sure your repository extends from `RevisionRepository` as show in the following example: + +[source,java,indent=0,subs="verbatim"] +---- +include::{docs-java}/features/sql/jpaandspringdata/repositories/CountryRepository.java[] +---- + +NOTE: For more details, check the {spring-data-envers-doc}[Spring Data Envers reference documentation]. + + + [[features.sql.jpa-and-spring-data.creating-and-dropping]] ==== Creating and Dropping JPA Databases By default, JPA databases are automatically created *only* if you use an embedded database (H2, HSQL, or Derby). diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/sql/jpaandspringdata/entityclasses/Country.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/sql/jpaandspringdata/entityclasses/Country.java new file mode 100644 index 00000000000..33f5ef66394 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/sql/jpaandspringdata/entityclasses/Country.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-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 org.springframework.boot.docs.features.sql.jpaandspringdata.entityclasses; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.envers.Audited; + +@Entity +public class Country implements Serializable { + + @Id + @GeneratedValue + private Long id; + + @Audited + @Column(nullable = false) + private String name; + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/sql/jpaandspringdata/repositories/CountryRepository.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/sql/jpaandspringdata/repositories/CountryRepository.java new file mode 100644 index 00000000000..497ec799ec9 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/sql/jpaandspringdata/repositories/CountryRepository.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-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 org.springframework.boot.docs.features.sql.jpaandspringdata.repositories; + +import org.springframework.boot.docs.features.sql.jpaandspringdata.entityclasses.Country; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.history.RevisionRepository; + +public interface CountryRepository extends RevisionRepository, Repository { + + Page findAll(Pageable pageable); + +}