Skip to content

Add auto-configuration for Spring Data Envers #22610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions spring-boot-project/spring-boot-autoconfigure/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ dependencies {
optional("org.springframework.batch:spring-batch-core")
optional("org.springframework.data:spring-data-couchbase")
optional("org.springframework.data:spring-data-jpa")
optional("org.springframework.data:spring-data-envers")
optional("org.springframework.data:spring-data-rest-webmvc")
optional("org.springframework.data:spring-data-cassandra")
optional("org.springframework.data:spring-data-elasticsearch") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2012-2020 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 {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,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.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
Expand All @@ -35,28 +36,35 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.task.AsyncTaskExecutor;
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;

/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's JPA Repositories.
* <p>
* Activates when there is a bean of type {@link javax.sql.DataSource} configured in the
* context, the Spring Data JPA
* {@link org.springframework.data.jpa.repository.JpaRepository} type is on the classpath,
* and there is no other, existing
* {@link org.springframework.data.jpa.repository.JpaRepository} configured.
* context, the Spring Data JPA {@link JpaRepository} type is on the classpath, and there
* is no other, existing {@link JpaRepository} configured.
* <p>
* Once in effect, the auto-configuration is the equivalent of enabling JPA repositories
* using the {@link EnableJpaRepositories @EnableJpaRepositories} annotation.
* <p>
* In case {@link EnableEnversRepositories} is on the classpath,
* {@link EnversRevisionRepositoryFactoryBean} is used instead of
* {@link JpaRepositoryFactoryBean} to support {@link RevisionRepository} with Hibernate
* Envers.
* <p>
* This configuration class will activate <em>after</em> the Hibernate auto-configuration.
*
* @author Phillip Webb
* @author Josh Long
* @author Scott Frederick
* @author Stefano Cordio
* @since 1.0.0
* @see EnableJpaRepositories
*/
Expand All @@ -66,7 +74,6 @@
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true",
matchIfMissing = true)
@Import(JpaRepositoriesRegistrar.class)
@AutoConfigureAfter({ HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
public class JpaRepositoriesAutoConfiguration {

Expand Down Expand Up @@ -108,4 +115,18 @@ static class LazyBootstrapMode {

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(EnableEnversRepositories.class)
@Import(EnversRevisionRepositoriesRegistrar.class)
public static class EnversRevisionRepositoriesRegistrarConfiguration {

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.data.envers.repository.config.EnableEnversRepositories")
@Import(JpaRepositoriesRegistrar.class)
public static class JpaRepositoriesRegistrarConfiguration {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* Copyright 2012-2020 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 {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2012-2020 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));
}

}
Loading