Add @Conditionals to permit JPA/Mongo mixed usage

I decided to go with both approaches (make the autoconfig for
repositories @ConditionalOnMissingBean(RepositoryFactoryBeanSupport),
so the first one wins; and also make them conditional on
spring.data.*.repositories.enabled=true. The ordering problem
is still there really (it's not defined which repositories will
be created by the autoconfig), so if a user is going to have
2 repository implementations on the classpath, he is going to
have to either choose one to disable, or manualy @Enable* the
other one.

Fixes gh-1042
This commit is contained in:
Dave Syer 2014-06-09 09:36:00 +01:00
parent 83694a09b3
commit 39a94428d3
7 changed files with 110 additions and 16 deletions

View File

@ -19,12 +19,13 @@ package org.springframework.boot.autoconfigure.data.elasticsearch;
import org.elasticsearch.client.Client;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactoryBean;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Elasticsearch
@ -37,7 +38,8 @@ import org.springframework.data.elasticsearch.repository.support.ElasticsearchRe
*/
@Configuration
@ConditionalOnClass({ Client.class, ElasticsearchRepository.class })
@ConditionalOnMissingBean(ElasticsearchRepositoryFactoryBean.class)
@ConditionalOnExpression("${spring.data.elasticsearch.repositories.enabled:true}")
@ConditionalOnMissingBean(RepositoryFactoryBeanSupport.class)
@Import(ElasticsearchRepositoriesAutoConfigureRegistrar.class)
public class ElasticsearchRepositoriesAutoConfiguration {

View File

@ -22,6 +22,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
@ -29,7 +30,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
@ -55,7 +56,8 @@ import org.springframework.data.web.config.EnableSpringDataWebSupport;
@Configuration
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean(JpaRepositoryFactoryBean.class)
@ConditionalOnMissingBean(RepositoryFactoryBeanSupport.class)
@ConditionalOnExpression("${spring.data.jpa.repositories.enabled:true}")
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration {

View File

@ -19,13 +19,14 @@ package org.springframework.boot.autoconfigure.data.mongo;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import com.mongodb.Mongo;
@ -52,7 +53,8 @@ import com.mongodb.Mongo;
*/
@Configuration
@ConditionalOnClass({ Mongo.class, MongoRepository.class })
@ConditionalOnMissingBean(MongoRepositoryFactoryBean.class)
@ConditionalOnMissingBean(RepositoryFactoryBeanSupport.class)
@ConditionalOnExpression("${spring.data.mongo.repositories.enabled:true}")
@Import(MongoRepositoriesAutoConfigureRegistrar.class)
@AutoConfigureAfter(MongoAutoConfiguration.class)
public class MongoRepositoriesAutoConfiguration {

View File

@ -18,11 +18,12 @@ package org.springframework.boot.autoconfigure.data.solr;
import org.apache.solr.client.solrj.SolrServer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.data.solr.repository.SolrRepository;
import org.springframework.data.solr.repository.support.SolrRepositoryFactoryBean;
/**
* Enables auto configuration for Spring Data Solr repositories.
@ -42,7 +43,8 @@ import org.springframework.data.solr.repository.support.SolrRepositoryFactoryBea
*/
@Configuration
@ConditionalOnClass({ SolrServer.class, SolrRepository.class })
@ConditionalOnMissingBean(SolrRepositoryFactoryBean.class)
@ConditionalOnMissingBean(RepositoryFactoryBeanSupport.class)
@ConditionalOnExpression("${spring.data.solr.repositories.enabled:true}")
@Import(SolrRepositoriesAutoConfigureRegistrar.class)
public class SolrRepositoriesAutoConfiguration {

View File

@ -18,9 +18,9 @@ package org.springframework.boot.autoconfigure.data.jpa.city;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CityRepository extends Repository<City, Long> {
public interface CityRepository extends JpaRepository<City, Long> {
Page<City> findAll(Pageable pageable);

View File

@ -16,12 +16,16 @@
package org.springframework.boot.autoconfigure.data.mongo;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.data.jpa.city.City;
import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository;
import org.springframework.boot.autoconfigure.data.mongo.MixedMongoRepositoriesAutoConfigurationTests.BaseConfiguration.Registrar;
import org.springframework.boot.autoconfigure.data.mongo.country.Country;
import org.springframework.boot.autoconfigure.data.mongo.country.CountryRepository;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@ -34,6 +38,8 @@ import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@ -75,13 +81,57 @@ public class MixedMongoRepositoriesAutoConfigurationTests {
assertNotNull(this.context.getBean(CityRepository.class));
}
@Test
public void testJpaRepositoryConfigurationWithMongoTemplate() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.initialize:false");
this.context.register(JpaConfiguration.class, BaseConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(CityRepository.class));
}
@Test
public void testJpaRepositoryConfigurationWithMongoOverlap() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.initialize:false");
this.context.register(OverlapConfiguration.class, BaseConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(CityRepository.class));
}
@Test
public void testJpaRepositoryConfigurationWithMongoOverlapDisabled() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.initialize:false",
"spring.data.mongo.repositories.enabled:false");
this.context.register(OverlapConfiguration.class, BaseConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(CityRepository.class));
}
@Configuration
@Import({ MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoRepositoriesAutoConfiguration.class, DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class })
@Import(Registrar.class)
protected static class BaseConfiguration {
protected static class Registrar implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
List<String> names = new ArrayList<String>();
for (Class<?> type : new Class<?>[] { DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoRepositoriesAutoConfiguration.class }) {
names.add(type.getName());
}
return names.toArray(new String[0]);
}
}
}
@Configuration
@ -100,4 +150,21 @@ public class MixedMongoRepositoriesAutoConfigurationTests {
protected static class MixedConfiguration {
}
@Configuration
@TestAutoConfigurationPackage(MongoAutoConfigurationTests.class)
@EntityScan(basePackageClasses = City.class)
@EnableJpaRepositories(basePackageClasses = CityRepository.class)
protected static class JpaConfiguration {
}
// In this one the Jpa repositories and the autoconfiguration packages overlap, so
// Mongo will try and configure the same repositories
@Configuration
@TestAutoConfigurationPackage(CityRepository.class)
@EnableJpaRepositories(basePackageClasses = CityRepository.class)
protected static class OverlapConfiguration {
}
}

View File

@ -1004,7 +1004,6 @@ Spring Boot tries to guess the location of your `@Repository` definitions, based
annotation (from Spring Data JPA).
[[howto-separate-entity-definitions-from-spring-configuration]]
=== Separate @Entity definitions from Spring configuration
Spring Boot tries to guess the location of your `@Entity` definitions, based on the
@ -1116,6 +1115,26 @@ See
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java[`JpaBaseConfiguration`]
for the default settings.
[[howto-use-spring-data-jpa--and-mongo-repositories]]
=== Use Spring Data JPA and Mongo repositories
Spring Data JPA and Spring Data Mongo can both create `Repository`
implementations for you automatically. If they are both present on the
classpath, you might have to do some extra configuration to tell
Spring Boot which one (or both) you want to create repositories for
you. The most explicit way to do that is to use the standard Spring
Data `@Enable*Repositories` and tell it the location of your
`Repository` interfaces (where "*" is "Jpa" or "Mongo" or both).
There are also flags `spring.data.*.repositories.enabled` that you can
use to switch the autoconfigured repositories on and off in external
configuration. This is useful for instance in case you want to switch
off the Mongo repositories and still use the autoconfigured
`MongoTemplate`.
The same obstacle and the same features exist for other autoconfigured
Spring Data repository types (Elasticsearch, Solr). Just change the
names of the annotations and flags respectively.
[[howto-database-initialization]]