Skip to content

Commit 39a9442

Browse files
author
Dave Syer
committed
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
1 parent 83694a0 commit 39a9442

File tree

7 files changed

+110
-16
lines changed

7 files changed

+110
-16
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchRepositoriesAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
import org.elasticsearch.client.Client;
2020
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2121
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
2223
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2324
import org.springframework.context.annotation.Configuration;
2425
import org.springframework.context.annotation.Import;
2526
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
2627
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
27-
import org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactoryBean;
28+
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
2829

2930
/**
3031
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Elasticsearch
@@ -37,7 +38,8 @@
3738
*/
3839
@Configuration
3940
@ConditionalOnClass({ Client.class, ElasticsearchRepository.class })
40-
@ConditionalOnMissingBean(ElasticsearchRepositoryFactoryBean.class)
41+
@ConditionalOnExpression("${spring.data.elasticsearch.repositories.enabled:true}")
42+
@ConditionalOnMissingBean(RepositoryFactoryBeanSupport.class)
4143
@Import(ElasticsearchRepositoriesAutoConfigureRegistrar.class)
4244
public class ElasticsearchRepositoriesAutoConfiguration {
4345

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jpa/JpaRepositoriesAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@
2222
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2323
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2424
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
2526
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2627
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
2728
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
2829
import org.springframework.context.annotation.Configuration;
2930
import org.springframework.context.annotation.Import;
3031
import org.springframework.data.jpa.repository.JpaRepository;
3132
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
32-
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
33+
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
3334
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
3435
import org.springframework.data.web.config.EnableSpringDataWebSupport;
3536

@@ -55,7 +56,8 @@
5556
@Configuration
5657
@ConditionalOnBean(DataSource.class)
5758
@ConditionalOnClass(JpaRepository.class)
58-
@ConditionalOnMissingBean(JpaRepositoryFactoryBean.class)
59+
@ConditionalOnMissingBean(RepositoryFactoryBeanSupport.class)
60+
@ConditionalOnExpression("${spring.data.jpa.repositories.enabled:true}")
5961
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
6062
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
6163
public class JpaRepositoriesAutoConfiguration {

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2020
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2121
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
2223
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2324
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
2425
import org.springframework.context.annotation.Configuration;
2526
import org.springframework.context.annotation.Import;
2627
import org.springframework.data.mongodb.repository.MongoRepository;
2728
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
28-
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean;
29+
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
2930

3031
import com.mongodb.Mongo;
3132

@@ -52,7 +53,8 @@
5253
*/
5354
@Configuration
5455
@ConditionalOnClass({ Mongo.class, MongoRepository.class })
55-
@ConditionalOnMissingBean(MongoRepositoryFactoryBean.class)
56+
@ConditionalOnMissingBean(RepositoryFactoryBeanSupport.class)
57+
@ConditionalOnExpression("${spring.data.mongo.repositories.enabled:true}")
5658
@Import(MongoRepositoriesAutoConfigureRegistrar.class)
5759
@AutoConfigureAfter(MongoAutoConfiguration.class)
5860
public class MongoRepositoriesAutoConfiguration {

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/solr/SolrRepositoriesAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818

1919
import org.apache.solr.client.solrj.SolrServer;
2020
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
2122
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2223
import org.springframework.context.annotation.Configuration;
2324
import org.springframework.context.annotation.Import;
25+
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
2426
import org.springframework.data.solr.repository.SolrRepository;
25-
import org.springframework.data.solr.repository.support.SolrRepositoryFactoryBean;
2627

2728
/**
2829
* Enables auto configuration for Spring Data Solr repositories.
@@ -42,7 +43,8 @@
4243
*/
4344
@Configuration
4445
@ConditionalOnClass({ SolrServer.class, SolrRepository.class })
45-
@ConditionalOnMissingBean(SolrRepositoryFactoryBean.class)
46+
@ConditionalOnMissingBean(RepositoryFactoryBeanSupport.class)
47+
@ConditionalOnExpression("${spring.data.solr.repositories.enabled:true}")
4648
@Import(SolrRepositoriesAutoConfigureRegistrar.class)
4749
public class SolrRepositoriesAutoConfiguration {
4850

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jpa/city/CityRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818

1919
import org.springframework.data.domain.Page;
2020
import org.springframework.data.domain.Pageable;
21-
import org.springframework.data.repository.Repository;
21+
import org.springframework.data.jpa.repository.JpaRepository;
2222

23-
public interface CityRepository extends Repository<City, Long> {
23+
public interface CityRepository extends JpaRepository<City, Long> {
2424

2525
Page<City> findAll(Pageable pageable);
2626

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616

1717
package org.springframework.boot.autoconfigure.data.mongo;
1818

19+
import java.util.ArrayList;
20+
import java.util.List;
21+
1922
import org.junit.After;
2023
import org.junit.Test;
21-
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
2224
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
25+
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
2326
import org.springframework.boot.autoconfigure.data.jpa.city.City;
2427
import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository;
28+
import org.springframework.boot.autoconfigure.data.mongo.MixedMongoRepositoriesAutoConfigurationTests.BaseConfiguration.Registrar;
2529
import org.springframework.boot.autoconfigure.data.mongo.country.Country;
2630
import org.springframework.boot.autoconfigure.data.mongo.country.CountryRepository;
2731
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@@ -34,6 +38,8 @@
3438
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3539
import org.springframework.context.annotation.Configuration;
3640
import org.springframework.context.annotation.Import;
41+
import org.springframework.context.annotation.ImportSelector;
42+
import org.springframework.core.type.AnnotationMetadata;
3743
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
3844
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
3945

@@ -75,13 +81,57 @@ public void testMixedRepositoryConfiguration() throws Exception {
7581
assertNotNull(this.context.getBean(CityRepository.class));
7682
}
7783

84+
@Test
85+
public void testJpaRepositoryConfigurationWithMongoTemplate() throws Exception {
86+
this.context = new AnnotationConfigApplicationContext();
87+
EnvironmentTestUtils.addEnvironment(this.context,
88+
"spring.datasource.initialize:false");
89+
this.context.register(JpaConfiguration.class, BaseConfiguration.class);
90+
this.context.refresh();
91+
assertNotNull(this.context.getBean(CityRepository.class));
92+
}
93+
94+
@Test
95+
public void testJpaRepositoryConfigurationWithMongoOverlap() throws Exception {
96+
this.context = new AnnotationConfigApplicationContext();
97+
EnvironmentTestUtils.addEnvironment(this.context,
98+
"spring.datasource.initialize:false");
99+
this.context.register(OverlapConfiguration.class, BaseConfiguration.class);
100+
this.context.refresh();
101+
assertNotNull(this.context.getBean(CityRepository.class));
102+
}
103+
104+
@Test
105+
public void testJpaRepositoryConfigurationWithMongoOverlapDisabled() throws Exception {
106+
this.context = new AnnotationConfigApplicationContext();
107+
EnvironmentTestUtils.addEnvironment(this.context,
108+
"spring.datasource.initialize:false",
109+
"spring.data.mongo.repositories.enabled:false");
110+
this.context.register(OverlapConfiguration.class, BaseConfiguration.class);
111+
this.context.refresh();
112+
assertNotNull(this.context.getBean(CityRepository.class));
113+
}
114+
78115
@Configuration
79-
@Import({ MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
80-
MongoRepositoriesAutoConfiguration.class, DataSourceAutoConfiguration.class,
81-
HibernateJpaAutoConfiguration.class,
82-
PropertyPlaceholderAutoConfiguration.class })
116+
@Import(Registrar.class)
83117
protected static class BaseConfiguration {
84118

119+
protected static class Registrar implements ImportSelector {
120+
121+
@Override
122+
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
123+
List<String> names = new ArrayList<String>();
124+
for (Class<?> type : new Class<?>[] { DataSourceAutoConfiguration.class,
125+
HibernateJpaAutoConfiguration.class,
126+
JpaRepositoriesAutoConfiguration.class,
127+
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
128+
MongoRepositoriesAutoConfiguration.class }) {
129+
names.add(type.getName());
130+
}
131+
return names.toArray(new String[0]);
132+
}
133+
}
134+
85135
}
86136

87137
@Configuration
@@ -100,4 +150,21 @@ protected static class TestConfiguration {
100150
protected static class MixedConfiguration {
101151

102152
}
153+
154+
@Configuration
155+
@TestAutoConfigurationPackage(MongoAutoConfigurationTests.class)
156+
@EntityScan(basePackageClasses = City.class)
157+
@EnableJpaRepositories(basePackageClasses = CityRepository.class)
158+
protected static class JpaConfiguration {
159+
160+
}
161+
162+
// In this one the Jpa repositories and the autoconfiguration packages overlap, so
163+
// Mongo will try and configure the same repositories
164+
@Configuration
165+
@TestAutoConfigurationPackage(CityRepository.class)
166+
@EnableJpaRepositories(basePackageClasses = CityRepository.class)
167+
protected static class OverlapConfiguration {
168+
169+
}
103170
}

spring-boot-docs/src/main/asciidoc/howto.adoc

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,6 @@ Spring Boot tries to guess the location of your `@Repository` definitions, based
10041004
annotation (from Spring Data JPA).
10051005

10061006

1007-
10081007
[[howto-separate-entity-definitions-from-spring-configuration]]
10091008
=== Separate @Entity definitions from Spring configuration
10101009
Spring Boot tries to guess the location of your `@Entity` definitions, based on the
@@ -1116,6 +1115,26 @@ See
11161115
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java[`JpaBaseConfiguration`]
11171116
for the default settings.
11181117

1118+
[[howto-use-spring-data-jpa--and-mongo-repositories]]
1119+
=== Use Spring Data JPA and Mongo repositories
1120+
1121+
Spring Data JPA and Spring Data Mongo can both create `Repository`
1122+
implementations for you automatically. If they are both present on the
1123+
classpath, you might have to do some extra configuration to tell
1124+
Spring Boot which one (or both) you want to create repositories for
1125+
you. The most explicit way to do that is to use the standard Spring
1126+
Data `@Enable*Repositories` and tell it the location of your
1127+
`Repository` interfaces (where "*" is "Jpa" or "Mongo" or both).
1128+
1129+
There are also flags `spring.data.*.repositories.enabled` that you can
1130+
use to switch the autoconfigured repositories on and off in external
1131+
configuration. This is useful for instance in case you want to switch
1132+
off the Mongo repositories and still use the autoconfigured
1133+
`MongoTemplate`.
1134+
1135+
The same obstacle and the same features exist for other autoconfigured
1136+
Spring Data repository types (Elasticsearch, Solr). Just change the
1137+
names of the annotations and flags respectively.
11191138

11201139

11211140
[[howto-database-initialization]]

0 commit comments

Comments
 (0)