diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBean.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBean.java index dbbd850369..3256c97795 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBean.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBean.java @@ -25,6 +25,7 @@ import java.lang.annotation.Target; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -69,30 +70,37 @@ public @interface ConditionalOnBean { /** * The class types of beans that should be checked. The condition matches when beans * of all classes specified are contained in the {@link BeanFactory}. Beans that are - * not autowire candidates are ignored. + * not autowire candidates or that are not default candidates are ignored. * @return the class types of beans to check * @see Bean#autowireCandidate() * @see BeanDefinition#isAutowireCandidate + * @see Bean#defaultCandidate() + * @see AbstractBeanDefinition#isDefaultCandidate */ Class[] value() default {}; /** * The class type names of beans that should be checked. The condition matches when * beans of all classes specified are contained in the {@link BeanFactory}. Beans that - * are not autowire candidates are ignored. + * are not autowire candidates or that are not default candidates are ignored. * @return the class type names of beans to check * @see Bean#autowireCandidate() * @see BeanDefinition#isAutowireCandidate + * @see Bean#defaultCandidate() + * @see AbstractBeanDefinition#isDefaultCandidate */ String[] type() default {}; /** * The annotation type decorating a bean that should be checked. The condition matches * when all the annotations specified are defined on beans in the {@link BeanFactory}. - * Beans that are not autowire candidates are ignored. + * Beans that are not autowire candidates or that are not default candidates are + * ignored. * @return the class-level annotation types to check * @see Bean#autowireCandidate() * @see BeanDefinition#isAutowireCandidate + * @see Bean#defaultCandidate() + * @see AbstractBeanDefinition#isDefaultCandidate */ Class[] annotation() default {}; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBean.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBean.java index 60f721f241..be71a1adac 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBean.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBean.java @@ -25,6 +25,7 @@ import java.lang.annotation.Target; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -70,20 +71,24 @@ public @interface ConditionalOnMissingBean { /** * The class types of beans that should be checked. The condition matches when no bean * of each class specified is contained in the {@link BeanFactory}. Beans that are not - * autowire candidates are ignored. + * autowire candidates or that are not default candidates are ignored. * @return the class types of beans to check * @see Bean#autowireCandidate() * @see BeanDefinition#isAutowireCandidate + * @see Bean#defaultCandidate() + * @see AbstractBeanDefinition#isDefaultCandidate */ Class[] value() default {}; /** * The class type names of beans that should be checked. The condition matches when no * bean of each class specified is contained in the {@link BeanFactory}. Beans that - * are not autowire candidates are ignored. + * are not autowire candidates or that are not default candidates are ignored. * @return the class type names of beans to check * @see Bean#autowireCandidate() * @see BeanDefinition#isAutowireCandidate + * @see Bean#defaultCandidate() + * @see AbstractBeanDefinition#isDefaultCandidate */ String[] type() default {}; @@ -105,10 +110,13 @@ public @interface ConditionalOnMissingBean { /** * The annotation type decorating a bean that should be checked. The condition matches * when each annotation specified is missing from all beans in the - * {@link BeanFactory}. Beans that are not autowire candidates are ignored. + * {@link BeanFactory}. Beans that are not autowire candidates or that are not default + * candidates are ignored. * @return the class-level annotation types to check * @see Bean#autowireCandidate() * @see BeanDefinition#isAutowireCandidate + * @see Bean#defaultCandidate() + * @see AbstractBeanDefinition#isDefaultCandidate */ Class[] annotation() default {}; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidate.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidate.java index 43866f3ceb..cb99322311 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidate.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidate.java @@ -24,6 +24,7 @@ import java.lang.annotation.Target; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -53,14 +54,16 @@ public @interface ConditionalOnSingleCandidate { /** * The class type of bean that should be checked. The condition matches if a bean of * the class specified is contained in the {@link BeanFactory} and a primary candidate - * exists in case of multiple instances. Beans that are not autowire candidates are - * ignored. + * exists in case of multiple instances. Beans that are not autowire candidates or + * that are not default candidates are ignored. *

* This attribute may not be used in conjunction with * {@link #type()}, but it may be used instead of {@link #type()}. * @return the class type of the bean to check * @see Bean#autowireCandidate() * @see BeanDefinition#isAutowireCandidate + * @see Bean#defaultCandidate() + * @see AbstractBeanDefinition#isDefaultCandidate */ Class value() default Object.class; @@ -68,13 +71,15 @@ public @interface ConditionalOnSingleCandidate { * The class type name of bean that should be checked. The condition matches if a bean * of the class specified is contained in the {@link BeanFactory} and a primary * candidate exists in case of multiple instances. Beans that are not autowire - * candidates are ignored. + * candidates or that are not default candidates are ignored. *

* This attribute may not be used in conjunction with * {@link #value()}, but it may be used instead of {@link #value()}. * @return the class type name of the bean to check * @see Bean#autowireCandidate() * @see BeanDefinition#isAutowireCandidate + * @see Bean#defaultCandidate() + * @see AbstractBeanDefinition#isDefaultCandidate */ String type() default ""; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java index 14478d14ee..dc73aaf572 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java @@ -42,6 +42,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.SingletonBeanRegistry; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigurationMetadata; import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; import org.springframework.context.annotation.Bean; @@ -217,8 +218,8 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat Map typeMatchedDefinitions = getBeanDefinitionsForType(classLoader, considerHierarchy, beanFactory, type, parameterizedContainers); Set typeMatchedNames = matchedNamesFrom(typeMatchedDefinitions, - (name, definition) -> !beansIgnoredByType.contains(name) && !ScopedProxyUtils.isScopedTarget(name) - && (definition == null || definition.isAutowireCandidate())); + (name, definition) -> isCandidate(name, definition, beansIgnoredByType) + && !ScopedProxyUtils.isScopedTarget(name)); if (typeMatchedNames.isEmpty()) { result.recordUnmatchedType(type); } @@ -230,8 +231,7 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat Map annotationMatchedDefinitions = getBeanDefinitionsForAnnotation(classLoader, beanFactory, annotation, considerHierarchy); Set annotationMatchedNames = matchedNamesFrom(annotationMatchedDefinitions, - (name, definition) -> !beansIgnoredByType.contains(name) - && (definition == null || definition.isAutowireCandidate())); + (name, definition) -> isCandidate(name, definition, beansIgnoredByType)); if (annotationMatchedNames.isEmpty()) { result.recordUnmatchedAnnotation(annotation); } @@ -262,6 +262,18 @@ class OnBeanCondition extends FilteringSpringBootCondition implements Configurat return matchedNames; } + private boolean isCandidate(String name, BeanDefinition definition, Set ignoredBeans) { + return (!ignoredBeans.contains(name)) + && (definition == null || (definition.isAutowireCandidate() && isDefaultCandidate(definition))); + } + + private boolean isDefaultCandidate(BeanDefinition definition) { + if (definition instanceof AbstractBeanDefinition abstractBeanDefinition) { + return abstractBeanDefinition.isDefaultCandidate(); + } + return true; + } + private Set getNamesOfBeansIgnoredByType(ClassLoader classLoader, ListableBeanFactory beanFactory, boolean considerHierarchy, Set ignoredTypes, Set> parameterizedContainers) { Set result = null; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBeanTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBeanTests.java index aa6729914d..5b2d540ca4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBeanTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBeanTests.java @@ -261,6 +261,25 @@ class ConditionalOnBeanTests { .run((context) -> assertThat(context).doesNotHaveBean("bar")); } + @Test + void conditionalOnBeanTypeIgnoresNotDefaultCandidateBean() { + this.contextRunner.withUserConfiguration(NotDefaultCandidateConfiguration.class, OnBeanClassConfiguration.class) + .run((context) -> assertThat(context).doesNotHaveBean("bar")); + } + + @Test + void conditionalOnBeanNameMatchesNotDefaultCandidateBean() { + this.contextRunner.withUserConfiguration(NotDefaultCandidateConfiguration.class, OnBeanNameConfiguration.class) + .run((context) -> assertThat(context).hasBean("bar")); + } + + @Test + void conditionalOnAnnotatedBeanIgnoresNotDefaultCandidateBean() { + this.contextRunner + .withUserConfiguration(AnnotatedNotDefaultCandidateConfig.class, OnAnnotationConfiguration.class) + .run((context) -> assertThat(context).doesNotHaveBean("bar")); + } + private Consumer exampleBeanRequirement(String... names) { return (context) -> { String[] beans = context.getBeanNamesForType(ExampleBean.class); @@ -356,6 +375,16 @@ class ConditionalOnBeanTests { } + @Configuration(proxyBeanMethods = false) + static class NotDefaultCandidateConfiguration { + + @Bean(defaultCandidate = false) + String foo() { + return "foo"; + } + + } + @Configuration(proxyBeanMethods = false) @ImportResource("org/springframework/boot/autoconfigure/condition/foo.xml") static class XmlConfiguration { @@ -569,6 +598,16 @@ class ConditionalOnBeanTests { } + @Configuration(proxyBeanMethods = false) + static class AnnotatedNotDefaultCandidateConfig { + + @Bean(defaultCandidate = false) + ExampleBean exampleBean() { + return new ExampleBean("value"); + } + + } + @TestAnnotation static class ExampleBean { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java index 5374d9dca9..830f2c17fc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java @@ -363,6 +363,25 @@ class ConditionalOnMissingBeanTests { .run((context) -> assertThat(context).hasBean("bar")); } + @Test + void typeBasedMatchingIgnoresBeanThatIsNotDefaultCandidate() { + this.contextRunner.withUserConfiguration(NotDefaultCandidateConfig.class, OnBeanTypeConfiguration.class) + .run((context) -> assertThat(context).hasBean("bar")); + } + + @Test + void nameBasedMatchingConsidersBeanThatIsNotDefaultCandidate() { + this.contextRunner.withUserConfiguration(NotDefaultCandidateConfig.class, OnBeanNameConfiguration.class) + .run((context) -> assertThat(context).doesNotHaveBean("bar")); + } + + @Test + void annotationBasedMatchingIgnoresBeanThatIsNotDefaultCandidateBean() { + this.contextRunner + .withUserConfiguration(AnnotatedNotDefaultCandidateConfig.class, OnAnnotationConfiguration.class) + .run((context) -> assertThat(context).hasBean("bar")); + } + private Consumer exampleBeanRequirement(String... names) { return (context) -> { String[] beans = context.getBeanNamesForType(ExampleBean.class); @@ -607,6 +626,16 @@ class ConditionalOnMissingBeanTests { } + @Configuration(proxyBeanMethods = false) + static class NotDefaultCandidateConfig { + + @Bean(defaultCandidate = false) + String foo() { + return "foo"; + } + + } + @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(name = "foo") static class HierarchyConsidered { @@ -780,6 +809,16 @@ class ConditionalOnMissingBeanTests { } + @Configuration(proxyBeanMethods = false) + static class AnnotatedNotDefaultCandidateConfig { + + @Bean(autowireCandidate = false) + ExampleBean exampleBean() { + return new ExampleBean("value"); + } + + } + @TestAnnotation static class ExampleBean { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidateTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidateTests.java index 2c268d75bf..6cb9bad7c9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidateTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidateTests.java @@ -184,6 +184,17 @@ class ConditionalOnSingleCandidateTests { }); } + @Test + void singleCandidateMultipleCandidatesOneDefaultCandidate() { + this.contextRunner + .withUserConfiguration(AlphaConfiguration.class, BravoNonDefaultConfiguration.class, + OnBeanSingleCandidateConfiguration.class) + .run((context) -> { + assertThat(context).hasBean("consumer"); + assertThat(context.getBean("consumer")).isEqualTo("alpha"); + }); + } + @Configuration(proxyBeanMethods = false) @ConditionalOnSingleCandidate(String.class) static class OnBeanSingleCandidateConfiguration { @@ -303,4 +314,14 @@ class ConditionalOnSingleCandidateTests { } + @Configuration(proxyBeanMethods = false) + static class BravoNonDefaultConfiguration { + + @Bean(defaultCandidate = false) + String bravo() { + return "bravo"; + } + + } + } diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/data-access.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/data-access.adoc index a85f619178..36d89cd965 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/data-access.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/data-access.adoc @@ -110,43 +110,41 @@ See xref:reference:data/sql.adoc#data.sql.datasource[] in the "`Spring Boot Feat [[howto.data-access.configure-two-datasources]] == Configure Two DataSources -If you need to configure multiple data sources, you can apply the same tricks that were described in the previous section. -You must, however, mark one of the `DataSource` instances as `@Primary`, because various auto-configurations down the road expect to be able to get one by type. +To define an additional `DataSource`, an approach that's similar to the previous section can be used. +A key difference is that the `DataSource` `@Bean` must be declared with `defaultCandidate=false`. +This prevents the auto-configured `DataSource` from backing off. +To allow the additional `DataSource` to be injected where it's needed, also annotate it with `@Qualifier` as shown in the following example: -If you create your own `DataSource`, the auto-configuration backs off. -In the following example, we provide the _exact_ same feature set as the auto-configuration provides on the primary data source: +include-code::MyAdditionalDataSourceConfiguration[] -include-code::MyDataSourcesConfiguration[] +To consume the additional `DataSource`, annotate the injection point with the same `@Qualifier`. -TIP: `firstDataSourceProperties` has to be flagged as `@Primary` so that the database initializer feature uses your copy (if you use the initializer). - -Both data sources are also bound for advanced customizations. -For instance, you could configure them as follows: +The auto-configured and additional data sources can be configured as follows: [configprops%novalidate,yaml] ---- +spring: + datasource: + url: "jdbc:mysql://localhost/first" + username: "dbuser" + password: "dbpass" + configuration: + maximum-pool-size: 30 app: datasource: - first: - url: "jdbc:mysql://localhost/first" - username: "dbuser" - password: "dbpass" - configuration: - maximum-pool-size: 30 - - second: - url: "jdbc:mysql://localhost/second" - username: "dbuser" - password: "dbpass" - max-total: 30 + url: "jdbc:mysql://localhost/second" + username: "dbuser" + password: "dbpass" + max-total: 30 ---- -You can apply the same concept to the secondary `DataSource` as well, as shown in the following example: +More advanced, implementation-specific, configuration of the auto-configured `DataSource` is available through the `spring.datasource.configuration.*` properties. +You can apply the same concept to the additional `DataSource` as well, as shown in the following example: -include-code::MyCompleteDataSourcesConfiguration[] +include-code::MyCompleteAdditionalDataSourceConfiguration[] -The preceding example configures two data sources on custom namespaces with the same logic as Spring Boot would use in auto-configuration. -Note that each `configuration` sub namespace provides advanced settings based on the chosen implementation. +The preceding example configures the additional data source with the same logic as Spring Boot would use in auto-configuration. +Note that the `app.datasource.configuration.*` properties provide advanced settings based on the chosen implementation. @@ -295,26 +293,28 @@ You can disable or tune this behavior by registering a `HibernatePropertiesCusto To take full control of the configuration of the `EntityManagerFactory`, you need to add a `@Bean` named '`entityManagerFactory`'. Spring Boot auto-configuration switches off its entity manager in the presence of a bean of that type. +NOTE: When you create a bean for `LocalContainerEntityManagerFactoryBean` yourself, any customization that was applied during the creation of the auto-configured `LocalContainerEntityManagerFactoryBean` is lost. +Make sure to use the auto-configured `EntityManagerFactoryBuilder` to retain JPA and vendor properties. +This is particularly important if you were relying on `spring.jpa.*` properties for configuring things like the naming strategy or the DDL mode. + [[howto.data-access.use-multiple-entity-managers]] == Using Multiple EntityManagerFactories -If you need to use JPA against multiple data sources, you likely need one `EntityManagerFactory` per data source. +If you need to use JPA against multiple datasources, you likely need one `EntityManagerFactory` per datasource. The `LocalContainerEntityManagerFactoryBean` from Spring ORM allows you to configure an `EntityManagerFactory` for your needs. -You can also reuse `JpaProperties` to bind settings for each `EntityManagerFactory`, as shown in the following example: +You can also reuse `JpaProperties` to bind settings for a second `EntityManagerFactory`. +Building upon xref:how-to:data-access.adoc#howto.data-access.configure-two-datasources[the example for configuring a second `DataSource`], a second `EntityManagerFactory` can be defined as shown in the following example: -include-code::MyEntityManagerFactoryConfiguration[] +include-code::MyAdditionalEntityManagerFactoryConfiguration[] -The example above creates an `EntityManagerFactory` using a `DataSource` bean named `firstDataSource`. +The example above creates an `EntityManagerFactory` using the `DataSource` bean qualified with `@Qualifier("second")`. It scans entities located in the same package as `Order`. -It is possible to map additional JPA properties using the `app.first.jpa` namespace. +It is possible to map additional JPA properties using the `app.jpa` namespace. +The use of `@Bean(defaultCandidate=false)` allows the `secondJpaProperties` and `secondEntityManagerFactory` beans to be defined without interfering with auto-configured beans of the same type. -NOTE: When you create a bean for `LocalContainerEntityManagerFactoryBean` yourself, any customization that was applied during the creation of the auto-configured `LocalContainerEntityManagerFactoryBean` is lost. -Make sure to use the auto-configured `EntityManagerFactoryBuilder` to retain JPA and vendor properties. -This is particularly important if you were relying on `spring.jpa.*` properties for configuring things like the naming strategy or the DDL mode. - -You should provide a similar configuration for any additional data sources for which you need JPA access. +You should provide a similar configuration for any more additional data sources for which you need JPA access. To complete the picture, you need to configure a `JpaTransactionManager` for each `EntityManagerFactory` as well. Alternatively, you might be able to use a JTA transaction manager that spans both. diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyAdditionalDataSourceConfiguration.java similarity index 59% rename from spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfiguration.java rename to spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyAdditionalDataSourceConfiguration.java index c88ba5a727..7780541c17 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfiguration.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyAdditionalDataSourceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 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. @@ -16,35 +16,20 @@ package org.springframework.boot.docs.howto.dataaccess.configuretwodatasources; -import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbcp2.BasicDataSource; -import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; @Configuration(proxyBeanMethods = false) -public class MyDataSourcesConfiguration { +public class MyAdditionalDataSourceConfiguration { - @Bean - @Primary - @ConfigurationProperties("app.datasource.first") - public DataSourceProperties firstDataSourceProperties() { - return new DataSourceProperties(); - } - - @Bean - @Primary - @ConfigurationProperties("app.datasource.first.configuration") - public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) { - return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); - } - - @Bean - @ConfigurationProperties("app.datasource.second") + @Qualifier("second") + @Bean(defaultCandidate = false) + @ConfigurationProperties("app.datasource") public BasicDataSource secondDataSource() { return DataSourceBuilder.create().type(BasicDataSource.class).build(); } diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteAdditionalDataSourceConfiguration.java similarity index 64% rename from spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfiguration.java rename to spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteAdditionalDataSourceConfiguration.java index f25837de72..829a9c413d 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfiguration.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteAdditionalDataSourceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 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. @@ -16,7 +16,6 @@ package org.springframework.boot.docs.howto.dataaccess.configuretwodatasources; -import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.springframework.beans.factory.annotation.Qualifier; @@ -24,33 +23,20 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; @Configuration(proxyBeanMethods = false) -public class MyCompleteDataSourcesConfiguration { +public class MyCompleteAdditionalDataSourceConfiguration { - @Bean - @Primary - @ConfigurationProperties("app.datasource.first") - public DataSourceProperties firstDataSourceProperties() { - return new DataSourceProperties(); - } - - @Bean - @Primary - @ConfigurationProperties("app.datasource.first.configuration") - public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) { - return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); - } - - @Bean - @ConfigurationProperties("app.datasource.second") + @Qualifier("second") + @Bean(defaultCandidate = false) + @ConfigurationProperties("app.datasource") public DataSourceProperties secondDataSourceProperties() { return new DataSourceProperties(); } - @Bean - @ConfigurationProperties("app.datasource.second.configuration") + @Qualifier("second") + @Bean(defaultCandidate = false) + @ConfigurationProperties("app.datasource.configuration") public BasicDataSource secondDataSource( @Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) { return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource.class).build(); diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyEntityManagerFactoryConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyAdditionalEntityManagerFactoryConfiguration.java similarity index 72% rename from spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyEntityManagerFactoryConfiguration.java rename to spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyAdditionalEntityManagerFactoryConfiguration.java index 11f898027b..8d26116a73 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyEntityManagerFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyAdditionalEntityManagerFactoryConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 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. @@ -18,6 +18,7 @@ package org.springframework.boot.docs.howto.dataaccess.usemultipleentitymanagers import javax.sql.DataSource; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; @@ -28,19 +29,21 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; @Configuration(proxyBeanMethods = false) -public class MyEntityManagerFactoryConfiguration { +public class MyAdditionalEntityManagerFactoryConfiguration { - @Bean - @ConfigurationProperties("app.jpa.first") - public JpaProperties firstJpaProperties() { + @Qualifier("second") + @Bean(defaultCandidate = false) + @ConfigurationProperties("app.jpa") + public JpaProperties secondJpaProperties() { return new JpaProperties(); } - @Bean - public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource, - JpaProperties firstJpaProperties) { - EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties); - return builder.dataSource(firstDataSource).packages(Order.class).persistenceUnit("firstDs").build(); + @Qualifier("second") + @Bean(defaultCandidate = false) + public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(@Qualifier("second") DataSource dataSource, + @Qualifier("second") JpaProperties jpaProperties) { + EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(jpaProperties); + return builder.dataSource(dataSource).packages(Order.class).persistenceUnit("second").build(); } private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) { diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/Order.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/Order.java index 333f4f615f..8f94cc06be 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/Order.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/Order.java @@ -16,6 +16,6 @@ package org.springframework.boot.docs.howto.dataaccess.usemultipleentitymanagers; -class Order { +public class Order { } diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/OrderConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/OrderConfiguration.java index 72f00ba0fb..7f86871cc4 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/OrderConfiguration.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/OrderConfiguration.java @@ -20,7 +20,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @Configuration(proxyBeanMethods = false) -@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory") +@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "entityManagerFactory") public class OrderConfiguration { } diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyAdditionalDataSourceConfiguration.kt similarity index 59% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyAdditionalDataSourceConfiguration.kt index 2e47fb8b10..401f08d7f4 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyAdditionalDataSourceConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -16,34 +16,20 @@ package org.springframework.boot.docs.howto.dataaccess.configuretwodatasources -import com.zaxxer.hikari.HikariDataSource import org.apache.commons.dbcp2.BasicDataSource -import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties + +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.jdbc.DataSourceBuilder import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.Primary @Configuration(proxyBeanMethods = false) -class MyDataSourcesConfiguration { +class MyAdditionalDataSourceConfiguration { - @Bean - @Primary - @ConfigurationProperties("app.datasource.first") - fun firstDataSourceProperties(): DataSourceProperties { - return DataSourceProperties() - } - - @Bean - @Primary - @ConfigurationProperties("app.datasource.first.configuration") - fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource { - return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build() - } - - @Bean - @ConfigurationProperties("app.datasource.second") + @Qualifier("second") + @Bean(defaultCandidate = false) + @ConfigurationProperties("app.datasource") fun secondDataSource(): BasicDataSource { return DataSourceBuilder.create().type(BasicDataSource::class.java).build() } diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteAdditionalDataSourceConfiguration.kt similarity index 62% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteAdditionalDataSourceConfiguration.kt index 24d142717a..cccbbff269 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteAdditionalDataSourceConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -16,39 +16,27 @@ package org.springframework.boot.docs.howto.dataaccess.configuretwodatasources -import com.zaxxer.hikari.HikariDataSource import org.apache.commons.dbcp2.BasicDataSource + +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.Primary @Configuration(proxyBeanMethods = false) -class MyCompleteDataSourcesConfiguration { +class MyCompleteAdditionalDataSourceConfiguration { - @Bean - @Primary - @ConfigurationProperties("app.datasource.first") - fun firstDataSourceProperties(): DataSourceProperties { - return DataSourceProperties() - } - - @Bean - @Primary - @ConfigurationProperties("app.datasource.first.configuration") - fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource { - return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build() - } - - @Bean - @ConfigurationProperties("app.datasource.second") + @Qualifier("second") + @Bean(defaultCandidate = false) + @ConfigurationProperties("app.datasource") fun secondDataSourceProperties(): DataSourceProperties { return DataSourceProperties() } - @Bean - @ConfigurationProperties("app.datasource.second.configuration") + @Qualifier("second") + @Bean(defaultCandidate = false) + @ConfigurationProperties("app.datasource.configuration") fun secondDataSource(secondDataSourceProperties: DataSourceProperties): BasicDataSource { return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource::class.java).build() } diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyEntityManagerFactoryConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyAdditionalEntityManagerFactoryConfiguration.kt similarity index 76% rename from spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyEntityManagerFactoryConfiguration.kt rename to spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyAdditionalEntityManagerFactoryConfiguration.kt index ef844b3ab3..55eefdd7e5 100644 --- a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyEntityManagerFactoryConfiguration.kt +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/dataaccess/usemultipleentitymanagers/MyAdditionalEntityManagerFactoryConfiguration.kt @@ -18,6 +18,7 @@ package org.springframework.boot.docs.howto.dataaccess.usemultipleentitymanagers import javax.sql.DataSource +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder @@ -29,21 +30,23 @@ import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter @Suppress("UNUSED_PARAMETER") @Configuration(proxyBeanMethods = false) -class MyEntityManagerFactoryConfiguration { +class MyAdditionalEntityManagerFactoryConfiguration { - @Bean - @ConfigurationProperties("app.jpa.first") - fun firstJpaProperties(): JpaProperties { + @Qualifier("second") + @Bean(defaultCandidate = false) + @ConfigurationProperties("app.jpa") + fun secondJpaProperties(): JpaProperties { return JpaProperties() } - @Bean + @Qualifier("second") + @Bean(defaultCandidate = false) fun firstEntityManagerFactory( - firstDataSource: DataSource?, - firstJpaProperties: JpaProperties + @Qualifier("second") dataSource: DataSource, + @Qualifier("second") jpaProperties: JpaProperties ): LocalContainerEntityManagerFactoryBean { - val builder = createEntityManagerFactoryBuilder(firstJpaProperties) - return builder.dataSource(firstDataSource).packages(Order::class.java).persistenceUnit("firstDs").build() + val builder = createEntityManagerFactoryBuilder(jpaProperties) + return builder.dataSource(dataSource).packages(Order::class.java).persistenceUnit("second").build() } private fun createEntityManagerFactoryBuilder(jpaProperties: JpaProperties): EntityManagerFactoryBuilder { diff --git a/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfigurationTests.java b/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfigurationTests.java index aa3be89a0c..143ab49855 100644 --- a/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfigurationTests.java +++ b/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyCompleteDataSourcesConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 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. @@ -21,37 +21,41 @@ import java.sql.SQLException; import javax.sql.DataSource; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link MyCompleteDataSourcesConfiguration}. + * Tests for {@link MyCompleteAdditionalDataSourceConfiguration}. * * @author Stephane Nicoll */ -@ExtendWith(SpringExtension.class) @SpringBootTest -@Import(MyCompleteDataSourcesConfiguration.class) +@Import(MyCompleteAdditionalDataSourceConfiguration.class) class MyCompleteDataSourcesConfigurationTests { @Autowired private ApplicationContext context; + @Autowired + private DataSource dataSource; + + @Autowired + @Qualifier("second") + private DataSource secondDataSource; + @Test void validateConfiguration() throws SQLException { assertThat(this.context.getBeansOfType(DataSource.class)).hasSize(2); - DataSource dataSource = this.context.getBean(DataSource.class); - assertThat(this.context.getBean("firstDataSource")).isSameAs(dataSource); - assertThat(dataSource.getConnection().getMetaData().getURL()).startsWith("jdbc:h2:mem:"); - DataSource secondDataSource = this.context.getBean("secondDataSource", DataSource.class); - assertThat(secondDataSource.getConnection().getMetaData().getURL()).startsWith("jdbc:h2:mem:"); + assertThat(this.context.getBean("dataSource")).isSameAs(this.dataSource); + assertThat(this.dataSource.getConnection().getMetaData().getURL()).startsWith("jdbc:h2:mem:"); + assertThat(this.context.getBean("secondDataSource")).isSameAs(this.secondDataSource); + assertThat(this.secondDataSource.getConnection().getMetaData().getURL()).startsWith("jdbc:h2:mem:"); } } diff --git a/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfigurationTests.java b/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfigurationTests.java index 6034bae765..cd0308777a 100644 --- a/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfigurationTests.java +++ b/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/howto/dataaccess/configuretwodatasources/MyDataSourcesConfigurationTests.java @@ -22,39 +22,44 @@ import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link MyDataSourcesConfiguration}. + * Tests for {@link MyAdditionalDataSourceConfiguration}. * * @author Stephane Nicoll */ -@ExtendWith(SpringExtension.class) -@SpringBootTest(properties = { "app.datasource.second.url=jdbc:h2:mem:bar;DB_CLOSE_DELAY=-1", - "app.datasource.second.max-total=42" }) -@Import(MyDataSourcesConfiguration.class) +@SpringBootTest(properties = { "app.datasource.url=jdbc:h2:mem:bar;DB_CLOSE_DELAY=-1", "app.datasource.max-total=42" }) +@Import(MyAdditionalDataSourceConfiguration.class) class MyDataSourcesConfigurationTests { @Autowired private ApplicationContext context; + @Autowired + private DataSource dataSource; + + @Autowired + @Qualifier("second") + private DataSource secondDataSource; + @Test void validateConfiguration() throws SQLException { assertThat(this.context.getBeansOfType(DataSource.class)).hasSize(2); - DataSource dataSource = this.context.getBean(DataSource.class); - assertThat(this.context.getBean("firstDataSource")).isSameAs(dataSource); - assertThat(dataSource.getConnection().getMetaData().getURL()).startsWith("jdbc:h2:mem:"); - BasicDataSource secondDataSource = this.context.getBean("secondDataSource", BasicDataSource.class); - assertThat(secondDataSource.getUrl()).isEqualTo("jdbc:h2:mem:bar;DB_CLOSE_DELAY=-1"); - assertThat(secondDataSource.getMaxTotal()).isEqualTo(42); + assertThat(this.context.getBean("dataSource")).isSameAs(this.dataSource); + assertThat(this.dataSource.getConnection().getMetaData().getURL()).startsWith("jdbc:h2:mem:"); + assertThat(this.context.getBean("secondDataSource")).isSameAs(this.secondDataSource); + assertThat(this.secondDataSource).extracting((dataSource) -> ((BasicDataSource) dataSource).getUrl()) + .isEqualTo("jdbc:h2:mem:bar;DB_CLOSE_DELAY=-1"); + assertThat(this.secondDataSource).extracting((dataSource) -> ((BasicDataSource) dataSource).getMaxTotal()) + .isEqualTo(42); } }