Skip to content

Commit 677a321

Browse files
committed
Support primary TxMgrs and DataSources in the TCF
Prior to this commit, the transaction manager and data source look-up algorithms in the Spring TestContext Framework were not capable of retrieving 'primary' beans of those types, even though 'primary' beans are supported in production as well as for injecting dependencies into test instances. Specifically, if there was more than one transaction manager or data source bean and one of them was flagged as 'primary', the retrieveTransactionManager() and retrieveDataSource() methods in TestContextTransactionUtils would simply return null for such beans. This commit updates TestContextTransactionUtils by adding support for looking up primary transaction managers and data sources. Issue: SPR-13891
1 parent 6d2b9a0 commit 677a321

File tree

3 files changed

+242
-10
lines changed

3 files changed

+242
-10
lines changed

spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.test.context.transaction;
1818

1919
import java.util.Map;
20+
2021
import javax.sql.DataSource;
2122

2223
import org.apache.commons.logging.Log;
@@ -73,7 +74,8 @@ private TestContextTransactionUtils() {
7374
* <li>Look up the {@code DataSource} by type and name, if the supplied
7475
* {@code name} is non-empty, throwing a {@link BeansException} if the named
7576
* {@code DataSource} does not exist.
76-
* <li>Attempt to look up a single {@code DataSource} by type.
77+
* <li>Attempt to look up the single {@code DataSource} by type.
78+
* <li>Attempt to look up the <em>primary</em> {@code DataSource} by type.
7779
* <li>Attempt to look up the {@code DataSource} by type and the
7880
* {@linkplain #DEFAULT_DATA_SOURCE_NAME default data source name}.
7981
* @param testContext the test context for which the {@code DataSource}
@@ -110,15 +112,21 @@ public static DataSource retrieveDataSource(TestContext testContext, String name
110112
if (dataSources.size() == 1) {
111113
return dataSources.values().iterator().next();
112114
}
115+
116+
try {
117+
// look up single bean by type, with support for 'primary' beans
118+
return bf.getBean(DataSource.class);
119+
}
120+
catch (BeansException ex) {
121+
logBeansException(testContext, ex, PlatformTransactionManager.class);
122+
}
113123
}
114124

115125
// look up by type and default name
116126
return bf.getBean(DEFAULT_DATA_SOURCE_NAME, DataSource.class);
117127
}
118128
catch (BeansException ex) {
119-
if (logger.isDebugEnabled()) {
120-
logger.debug("Caught exception while retrieving DataSource for test context " + testContext, ex);
121-
}
129+
logBeansException(testContext, ex, DataSource.class);
122130
return null;
123131
}
124132
}
@@ -133,7 +141,8 @@ public static DataSource retrieveDataSource(TestContext testContext, String name
133141
* <li>Look up the transaction manager by type and explicit name, if the supplied
134142
* {@code name} is non-empty, throwing a {@link BeansException} if the named
135143
* transaction manager does not exist.
136-
* <li>Attempt to look up the transaction manager by type.
144+
* <li>Attempt to look up the single transaction manager by type.
145+
* <li>Attempt to look up the <em>primary</em> transaction manager by type.
137146
* <li>Attempt to look up the transaction manager via a
138147
* {@link TransactionManagementConfigurer}, if present.
139148
* <li>Attempt to look up the transaction manager by type and the
@@ -176,6 +185,14 @@ public static PlatformTransactionManager retrieveTransactionManager(TestContext
176185
return txMgrs.values().iterator().next();
177186
}
178187

188+
try {
189+
// look up single bean by type, with support for 'primary' beans
190+
return bf.getBean(PlatformTransactionManager.class);
191+
}
192+
catch (BeansException ex) {
193+
logBeansException(testContext, ex, PlatformTransactionManager.class);
194+
}
195+
179196
// look up single TransactionManagementConfigurer
180197
Map<String, TransactionManagementConfigurer> configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors(
181198
lbf, TransactionManagementConfigurer.class);
@@ -192,14 +209,18 @@ public static PlatformTransactionManager retrieveTransactionManager(TestContext
192209
return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class);
193210
}
194211
catch (BeansException ex) {
195-
if (logger.isDebugEnabled()) {
196-
logger.debug("Caught exception while retrieving transaction manager for test context " + testContext,
197-
ex);
198-
}
212+
logBeansException(testContext, ex, PlatformTransactionManager.class);
199213
return null;
200214
}
201215
}
202216

217+
private static void logBeansException(TestContext testContext, BeansException ex, Class<?> beanType) {
218+
if (logger.isDebugEnabled()) {
219+
logger.debug(String.format("Caught exception while retrieving %s for test context %s",
220+
beanType.getSimpleName(), testContext), ex);
221+
}
222+
}
223+
203224
/**
204225
* Create a delegating {@link TransactionAttribute} for the supplied target
205226
* {@link TransactionAttribute} and {@link TestContext}, using the names of
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2002-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.jdbc;
18+
19+
import javax.sql.DataSource;
20+
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
24+
import org.springframework.beans.factory.annotation.Autowired;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.context.annotation.Primary;
28+
import org.springframework.jdbc.core.JdbcTemplate;
29+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
30+
import org.springframework.test.annotation.DirtiesContext;
31+
import org.springframework.test.context.ContextConfiguration;
32+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
33+
import org.springframework.test.jdbc.JdbcTestUtils;
34+
import org.springframework.test.transaction.TransactionTestUtils;
35+
36+
import static org.junit.Assert.*;
37+
38+
/**
39+
* Integration tests that ensure that <em>primary</em> data sources are
40+
* supported.
41+
*
42+
* @author Sam Brannen
43+
* @since 4.3
44+
* @see org.springframework.test.context.transaction.PrimaryTransactionManagerTests
45+
*/
46+
@RunWith(SpringJUnit4ClassRunner.class)
47+
@ContextConfiguration
48+
@DirtiesContext
49+
public class PrimaryDataSourceTests {
50+
51+
@Configuration
52+
static class Config {
53+
54+
@Primary
55+
@Bean
56+
public DataSource primaryDataSource() {
57+
// @formatter:off
58+
return new EmbeddedDatabaseBuilder()
59+
.generateUniqueName(true)
60+
.addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")
61+
.build();
62+
// @formatter:on
63+
}
64+
65+
@Bean
66+
public DataSource additionalDataSource() {
67+
return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
68+
}
69+
70+
}
71+
72+
73+
private JdbcTemplate jdbcTemplate;
74+
75+
76+
@Autowired
77+
public void setDataSource(DataSource dataSource) {
78+
this.jdbcTemplate = new JdbcTemplate(dataSource);
79+
}
80+
81+
@Test
82+
@Sql("data.sql")
83+
public void dataSourceTest() {
84+
TransactionTestUtils.assertInTransaction(false);
85+
assertEquals("Number of rows in the 'user' table.", 1,
86+
JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user"));
87+
}
88+
89+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright 2002-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.transaction;
18+
19+
import javax.sql.DataSource;
20+
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
24+
import org.springframework.beans.factory.annotation.Autowired;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.context.annotation.Primary;
28+
import org.springframework.core.io.ClassPathResource;
29+
import org.springframework.jdbc.core.JdbcTemplate;
30+
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
31+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
32+
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
33+
import org.springframework.test.annotation.DirtiesContext;
34+
import org.springframework.test.context.ContextConfiguration;
35+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
36+
import org.springframework.test.jdbc.JdbcTestUtils;
37+
import org.springframework.test.transaction.TransactionTestUtils;
38+
import org.springframework.transaction.PlatformTransactionManager;
39+
import org.springframework.transaction.annotation.Transactional;
40+
41+
import static org.junit.Assert.*;
42+
43+
/**
44+
* Integration tests that ensure that <em>primary</em> transaction managers
45+
* are supported.
46+
*
47+
* @author Sam Brannen
48+
* @since 4.3
49+
* @see org.springframework.test.context.jdbc.PrimaryDataSourceTests
50+
*/
51+
@RunWith(SpringJUnit4ClassRunner.class)
52+
@ContextConfiguration
53+
@DirtiesContext
54+
public class PrimaryTransactionManagerTests {
55+
56+
@Configuration
57+
static class Config {
58+
59+
@Primary
60+
@Bean
61+
public PlatformTransactionManager primaryTransactionManager() {
62+
return new DataSourceTransactionManager(dataSource1());
63+
}
64+
65+
@Bean
66+
public PlatformTransactionManager additionalTransactionManager() {
67+
return new DataSourceTransactionManager(dataSource2());
68+
}
69+
70+
@Bean
71+
public DataSource dataSource1() {
72+
// @formatter:off
73+
return new EmbeddedDatabaseBuilder()
74+
.generateUniqueName(true)
75+
.addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")
76+
.build();
77+
// @formatter:on
78+
}
79+
80+
@Bean
81+
public DataSource dataSource2() {
82+
return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
83+
}
84+
85+
}
86+
87+
88+
private JdbcTemplate jdbcTemplate;
89+
90+
91+
@Autowired
92+
public void setDataSource(DataSource dataSource1) {
93+
this.jdbcTemplate = new JdbcTemplate(dataSource1);
94+
}
95+
96+
@BeforeTransaction
97+
public void beforeTransaction() {
98+
assertNumUsers(0);
99+
}
100+
101+
@Test
102+
@Transactional
103+
public void transactionalTest() {
104+
TransactionTestUtils.assertInTransaction(true);
105+
106+
ClassPathResource resource = new ClassPathResource("/org/springframework/test/context/jdbc/data.sql");
107+
new ResourceDatabasePopulator(resource).execute(jdbcTemplate.getDataSource());
108+
109+
assertNumUsers(1);
110+
}
111+
112+
@AfterTransaction
113+
public void afterTransaction() {
114+
assertNumUsers(0);
115+
}
116+
117+
private void assertNumUsers(int expected) {
118+
assertEquals("Number of rows in the 'user' table.", expected,
119+
JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user"));
120+
}
121+
122+
}

0 commit comments

Comments
 (0)