Skip to content

Commit 3188f4f

Browse files
committed
Add JPA components only on single DataSource
Update auto-configuration logic so that JPA components are only added when there is a single candidate DataSource bean. Related with gh-6449
1 parent f38bd7a commit 3188f4f

File tree

5 files changed

+134
-91
lines changed

5 files changed

+134
-91
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java

Lines changed: 94 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
3131
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
3232
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
33+
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
3334
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
3435
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
3536
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition;
37+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3638
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
3739
import org.springframework.context.annotation.ConditionContext;
3840
import org.springframework.context.annotation.Conditional;
@@ -55,13 +57,15 @@
5557
* @author Josh Long
5658
* @author Manuel Doninger
5759
* @author Andy Wilkinson
60+
* @author Kazuki Shimizu
5861
*/
5962
@Configuration
6063
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class,
6164
EnableTransactionManagement.class, EntityManager.class })
6265
@Conditional(HibernateEntityManagerCondition.class)
6366
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
64-
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
67+
@EnableConfigurationProperties(JpaProperties.class)
68+
public class HibernateJpaAutoConfiguration {
6569

6670
private static final Log logger = LogFactory
6771
.getLog(HibernateJpaAutoConfiguration.class);
@@ -83,110 +87,117 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
8387
"org.hibernate.engine.transaction.jta.platform.internal.WebSphereExtendedJtaPlatform",
8488
"org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform", };
8589

86-
public HibernateJpaAutoConfiguration(DataSource dataSource,
87-
JpaProperties jpaProperties,
88-
ObjectProvider<JtaTransactionManager> jtaTransactionManagerProvider) {
89-
super(dataSource, jpaProperties, jtaTransactionManagerProvider);
90-
}
9190

92-
@Override
93-
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
94-
return new HibernateJpaVendorAdapter();
95-
}
91+
@Configuration
92+
@ConditionalOnSingleCandidate(DataSource.class)
93+
static class HibernateJpaConfiguration extends JpaBaseConfiguration {
9694

97-
@Override
98-
protected Map<String, Object> getVendorProperties() {
99-
Map<String, Object> vendorProperties = new LinkedHashMap<String, Object>();
100-
vendorProperties.putAll(getProperties().getHibernateProperties(getDataSource()));
101-
return vendorProperties;
102-
}
95+
HibernateJpaConfiguration(DataSource dataSource,
96+
JpaProperties jpaProperties,
97+
ObjectProvider<JtaTransactionManager> jtaTransactionManagerProvider) {
98+
super(dataSource, jpaProperties, jtaTransactionManagerProvider);
99+
}
100+
101+
@Override
102+
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
103+
return new HibernateJpaVendorAdapter();
104+
}
103105

104-
@Override
105-
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
106-
super.customizeVendorProperties(vendorProperties);
107-
if (!vendorProperties.containsKey(JTA_PLATFORM)) {
108-
configureJtaPlatform(vendorProperties);
106+
@Override
107+
protected Map<String, Object> getVendorProperties() {
108+
Map<String, Object> vendorProperties = new LinkedHashMap<String, Object>();
109+
vendorProperties.putAll(getProperties().getHibernateProperties(getDataSource()));
110+
return vendorProperties;
111+
}
112+
113+
@Override
114+
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
115+
super.customizeVendorProperties(vendorProperties);
116+
if (!vendorProperties.containsKey(JTA_PLATFORM)) {
117+
configureJtaPlatform(vendorProperties);
118+
}
109119
}
110-
}
111120

112-
private void configureJtaPlatform(Map<String, Object> vendorProperties)
113-
throws LinkageError {
114-
JtaTransactionManager jtaTransactionManager = getJtaTransactionManager();
115-
if (jtaTransactionManager != null) {
116-
if (runningOnWebSphere()) {
117-
// We can never use SpringJtaPlatform on WebSphere as
118-
// WebSphereUowTransactionManager has a null TransactionManager
119-
// which will cause Hibernate to NPE
120-
configureWebSphereTransactionPlatform(vendorProperties);
121+
private void configureJtaPlatform(Map<String, Object> vendorProperties)
122+
throws LinkageError {
123+
JtaTransactionManager jtaTransactionManager = getJtaTransactionManager();
124+
if (jtaTransactionManager != null) {
125+
if (runningOnWebSphere()) {
126+
// We can never use SpringJtaPlatform on WebSphere as
127+
// WebSphereUowTransactionManager has a null TransactionManager
128+
// which will cause Hibernate to NPE
129+
configureWebSphereTransactionPlatform(vendorProperties);
130+
}
131+
else {
132+
configureSpringJtaPlatform(vendorProperties, jtaTransactionManager);
133+
}
121134
}
122135
else {
123-
configureSpringJtaPlatform(vendorProperties, jtaTransactionManager);
136+
vendorProperties.put(JTA_PLATFORM, getNoJtaPlatformManager());
124137
}
125138
}
126-
else {
127-
vendorProperties.put(JTA_PLATFORM, getNoJtaPlatformManager());
128-
}
129-
}
130139

131-
private boolean runningOnWebSphere() {
132-
return ClassUtils.isPresent(
133-
"com.ibm.websphere.jtaextensions." + "ExtendedJTATransaction",
134-
getClass().getClassLoader());
135-
}
136-
137-
private void configureWebSphereTransactionPlatform(
138-
Map<String, Object> vendorProperties) {
139-
vendorProperties.put(JTA_PLATFORM, getWebSphereJtaPlatformManager());
140-
}
140+
private boolean runningOnWebSphere() {
141+
return ClassUtils.isPresent(
142+
"com.ibm.websphere.jtaextensions." + "ExtendedJTATransaction",
143+
getClass().getClassLoader());
144+
}
141145

142-
private Object getWebSphereJtaPlatformManager() {
143-
return getJtaPlatformManager(WEBSPHERE_JTA_PLATFORM_CLASSES);
144-
}
146+
private void configureWebSphereTransactionPlatform(
147+
Map<String, Object> vendorProperties) {
148+
vendorProperties.put(JTA_PLATFORM, getWebSphereJtaPlatformManager());
149+
}
145150

146-
private void configureSpringJtaPlatform(Map<String, Object> vendorProperties,
147-
JtaTransactionManager jtaTransactionManager) {
148-
try {
149-
vendorProperties.put(JTA_PLATFORM,
150-
new SpringJtaPlatform(jtaTransactionManager));
151+
private Object getWebSphereJtaPlatformManager() {
152+
return getJtaPlatformManager(WEBSPHERE_JTA_PLATFORM_CLASSES);
151153
}
152-
catch (LinkageError ex) {
153-
// NoClassDefFoundError can happen if Hibernate 4.2 is used and some
154-
// containers (e.g. JBoss EAP 6) wraps it in the superclass LinkageError
155-
if (!isUsingJndi()) {
156-
throw new IllegalStateException("Unable to set Hibernate JTA "
157-
+ "platform, are you using the correct "
158-
+ "version of Hibernate?", ex);
154+
155+
private void configureSpringJtaPlatform(Map<String, Object> vendorProperties,
156+
JtaTransactionManager jtaTransactionManager) {
157+
try {
158+
vendorProperties.put(JTA_PLATFORM,
159+
new SpringJtaPlatform(jtaTransactionManager));
159160
}
160-
// Assume that Hibernate will use JNDI
161-
if (logger.isDebugEnabled()) {
162-
logger.debug("Unable to set Hibernate JTA platform : " + ex.getMessage());
161+
catch (LinkageError ex) {
162+
// NoClassDefFoundError can happen if Hibernate 4.2 is used and some
163+
// containers (e.g. JBoss EAP 6) wraps it in the superclass LinkageError
164+
if (!isUsingJndi()) {
165+
throw new IllegalStateException("Unable to set Hibernate JTA "
166+
+ "platform, are you using the correct "
167+
+ "version of Hibernate?", ex);
168+
}
169+
// Assume that Hibernate will use JNDI
170+
if (logger.isDebugEnabled()) {
171+
logger.debug("Unable to set Hibernate JTA platform : " + ex.getMessage());
172+
}
163173
}
164174
}
165-
}
166175

167-
private boolean isUsingJndi() {
168-
try {
169-
return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable();
170-
}
171-
catch (Error ex) {
172-
return false;
176+
private boolean isUsingJndi() {
177+
try {
178+
return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable();
179+
}
180+
catch (Error ex) {
181+
return false;
182+
}
173183
}
174-
}
175184

176-
private Object getNoJtaPlatformManager() {
177-
return getJtaPlatformManager(NO_JTA_PLATFORM_CLASSES);
178-
}
185+
private Object getNoJtaPlatformManager() {
186+
return getJtaPlatformManager(NO_JTA_PLATFORM_CLASSES);
187+
}
179188

180-
private Object getJtaPlatformManager(String[] candidates) {
181-
for (String candidate : candidates) {
182-
try {
183-
return Class.forName(candidate).newInstance();
184-
}
185-
catch (Exception ex) {
186-
// Continue searching
189+
private Object getJtaPlatformManager(String[] candidates) {
190+
for (String candidate : candidates) {
191+
try {
192+
return Class.forName(candidate).newInstance();
193+
}
194+
catch (Exception ex) {
195+
// Continue searching
196+
}
187197
}
198+
throw new IllegalStateException("Could not configure JTA platform");
188199
}
189-
throw new IllegalStateException("Could not configure JTA platform");
200+
190201
}
191202

192203
@Order(Ordered.HIGHEST_PRECEDENCE + 20)

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3535
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
3636
import org.springframework.boot.autoconfigure.domain.EntityScanPackages;
37-
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3837
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
3938
import org.springframework.context.annotation.Bean;
4039
import org.springframework.context.annotation.Configuration;
@@ -59,8 +58,8 @@
5958
* @author Dave Syer
6059
* @author Oliver Gierke
6160
* @author Andy Wilkinson
61+
* @author Kazuki Shimizu
6262
*/
63-
@EnableConfigurationProperties(JpaProperties.class)
6463
@Import(DataSourceInitializedPublisher.Registrar.class)
6564
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
6665

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/MultiDataSourceConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* @author Kazuki Shimizu
2929
*/
3030
@Configuration
31-
class MultiDataSourceConfiguration {
31+
public class MultiDataSourceConfiguration {
3232

3333
@Bean
3434
public DataSource test1DataSource() {

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/MultiDataSourceUsingPrimaryConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* @author Kazuki Shimizu
3030
*/
3131
@Configuration
32-
class MultiDataSourceUsingPrimaryConfiguration {
32+
public class MultiDataSourceUsingPrimaryConfiguration {
3333

3434
@Bean
3535
@Primary

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@
2929
import org.junit.Test;
3030
import org.junit.rules.ExpectedException;
3131

32-
import org.springframework.beans.factory.BeanCreationException;
3332
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
3433
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
3534
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
3635
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
3736
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
37+
import org.springframework.boot.autoconfigure.jdbc.MultiDataSourceConfiguration;
38+
import org.springframework.boot.autoconfigure.jdbc.MultiDataSourceUsingPrimaryConfiguration;
3839
import org.springframework.boot.autoconfigure.orm.jpa.test.City;
40+
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
3941
import org.springframework.boot.test.util.EnvironmentTestUtils;
4042
import org.springframework.context.ApplicationContext;
4143
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -58,6 +60,7 @@
5860
*
5961
* @author Phillip Webb
6062
* @author Dave Syer
63+
* @author Kazuki Shimizu
6164
*/
6265
public abstract class AbstractJpaAutoConfigurationTests {
6366

@@ -77,10 +80,40 @@ public void close() {
7780
public void testNoDataSource() throws Exception {
7881
this.context.register(PropertyPlaceholderAutoConfiguration.class,
7982
getAutoConfigureClass());
80-
this.expected.expect(BeanCreationException.class);
81-
this.expected.expectMessage("No qualifying bean");
82-
this.expected.expectMessage("DataSource");
8383
this.context.refresh();
84+
assertThat(this.context.getBeansOfType(JpaProperties.class).size()).isEqualTo(1);
85+
assertThat(this.context.getBeansOfType(JpaVendorAdapter.class)).isEmpty();
86+
assertThat(this.context.getBeansOfType(EntityManagerFactoryBuilder.class)).isEmpty();
87+
assertThat(this.context.getBeansOfType(LocalContainerEntityManagerFactoryBean.class)).isEmpty();
88+
assertThat(this.context.getBeansOfType(PlatformTransactionManager.class)).isEmpty();
89+
}
90+
91+
@Test
92+
public void testMultiDataSource() throws Exception {
93+
this.context.register(PropertyPlaceholderAutoConfiguration.class,
94+
MultiDataSourceConfiguration.class,
95+
getAutoConfigureClass());
96+
this.context.refresh();
97+
assertThat(this.context.getBeansOfType(DataSource.class).size()).isEqualTo(2);
98+
assertThat(this.context.getBeansOfType(JpaProperties.class).size()).isEqualTo(1);
99+
assertThat(this.context.getBeansOfType(JpaVendorAdapter.class)).isEmpty();
100+
assertThat(this.context.getBeansOfType(EntityManagerFactoryBuilder.class)).isEmpty();
101+
assertThat(this.context.getBeansOfType(LocalContainerEntityManagerFactoryBean.class)).isEmpty();
102+
assertThat(this.context.getBeansOfType(PlatformTransactionManager.class)).isEmpty();
103+
}
104+
105+
@Test
106+
public void testMultiDataSourceUsingPrimary() throws Exception {
107+
this.context.register(PropertyPlaceholderAutoConfiguration.class,
108+
MultiDataSourceUsingPrimaryConfiguration.class,
109+
getAutoConfigureClass());
110+
this.context.refresh();
111+
assertThat(this.context.getBeansOfType(DataSource.class).size()).isEqualTo(2);
112+
assertThat(this.context.getBean(JpaProperties.class)).isNotNull();
113+
assertThat(this.context.getBean(JpaVendorAdapter.class)).isNotNull();
114+
assertThat(this.context.getBean(EntityManagerFactoryBuilder.class)).isNotNull();
115+
assertThat(this.context.getBean(LocalContainerEntityManagerFactoryBean.class)).isNotNull();
116+
assertThat(this.context.getBean(JpaTransactionManager.class)).isNotNull();
84117
}
85118

86119
@Test

0 commit comments

Comments
 (0)