From 677a321571e25449962befe876f543ce576ea3ca Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 3 Feb 2016 17:43:28 +0100 Subject: [PATCH] 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 --- .../TestContextTransactionUtils.java | 41 ++++-- .../context/jdbc/PrimaryDataSourceTests.java | 89 +++++++++++++ .../PrimaryTransactionManagerTests.java | 122 ++++++++++++++++++ 3 files changed, 242 insertions(+), 10 deletions(-) create mode 100644 spring-test/src/test/java/org/springframework/test/context/jdbc/PrimaryDataSourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java index 3b65d69056..b00a995587 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -17,6 +17,7 @@ package org.springframework.test.context.transaction; import java.util.Map; + import javax.sql.DataSource; import org.apache.commons.logging.Log; @@ -73,7 +74,8 @@ public abstract class TestContextTransactionUtils { *
  • Look up the {@code DataSource} by type and name, if the supplied * {@code name} is non-empty, throwing a {@link BeansException} if the named * {@code DataSource} does not exist. - *
  • Attempt to look up a single {@code DataSource} by type. + *
  • Attempt to look up the single {@code DataSource} by type. + *
  • Attempt to look up the primary {@code DataSource} by type. *
  • Attempt to look up the {@code DataSource} by type and the * {@linkplain #DEFAULT_DATA_SOURCE_NAME default data source name}. * @param testContext the test context for which the {@code DataSource} @@ -110,15 +112,21 @@ public abstract class TestContextTransactionUtils { if (dataSources.size() == 1) { return dataSources.values().iterator().next(); } + + try { + // look up single bean by type, with support for 'primary' beans + return bf.getBean(DataSource.class); + } + catch (BeansException ex) { + logBeansException(testContext, ex, PlatformTransactionManager.class); + } } // look up by type and default name return bf.getBean(DEFAULT_DATA_SOURCE_NAME, DataSource.class); } catch (BeansException ex) { - if (logger.isDebugEnabled()) { - logger.debug("Caught exception while retrieving DataSource for test context " + testContext, ex); - } + logBeansException(testContext, ex, DataSource.class); return null; } } @@ -133,7 +141,8 @@ public abstract class TestContextTransactionUtils { *
  • Look up the transaction manager by type and explicit name, if the supplied * {@code name} is non-empty, throwing a {@link BeansException} if the named * transaction manager does not exist. - *
  • Attempt to look up the transaction manager by type. + *
  • Attempt to look up the single transaction manager by type. + *
  • Attempt to look up the primary transaction manager by type. *
  • Attempt to look up the transaction manager via a * {@link TransactionManagementConfigurer}, if present. *
  • Attempt to look up the transaction manager by type and the @@ -176,6 +185,14 @@ public abstract class TestContextTransactionUtils { return txMgrs.values().iterator().next(); } + try { + // look up single bean by type, with support for 'primary' beans + return bf.getBean(PlatformTransactionManager.class); + } + catch (BeansException ex) { + logBeansException(testContext, ex, PlatformTransactionManager.class); + } + // look up single TransactionManagementConfigurer Map configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors( lbf, TransactionManagementConfigurer.class); @@ -192,14 +209,18 @@ public abstract class TestContextTransactionUtils { return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class); } catch (BeansException ex) { - if (logger.isDebugEnabled()) { - logger.debug("Caught exception while retrieving transaction manager for test context " + testContext, - ex); - } + logBeansException(testContext, ex, PlatformTransactionManager.class); return null; } } + private static void logBeansException(TestContext testContext, BeansException ex, Class beanType) { + if (logger.isDebugEnabled()) { + logger.debug(String.format("Caught exception while retrieving %s for test context %s", + beanType.getSimpleName(), testContext), ex); + } + } + /** * Create a delegating {@link TransactionAttribute} for the supplied target * {@link TransactionAttribute} and {@link TestContext}, using the names of diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/PrimaryDataSourceTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/PrimaryDataSourceTests.java new file mode 100644 index 0000000000..f07167806c --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/PrimaryDataSourceTests.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.jdbc; + +import javax.sql.DataSource; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.jdbc.JdbcTestUtils; +import org.springframework.test.transaction.TransactionTestUtils; + +import static org.junit.Assert.*; + +/** + * Integration tests that ensure that primary data sources are + * supported. + * + * @author Sam Brannen + * @since 4.3 + * @see org.springframework.test.context.transaction.PrimaryTransactionManagerTests + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class PrimaryDataSourceTests { + + @Configuration + static class Config { + + @Primary + @Bean + public DataSource primaryDataSource() { + // @formatter:off + return new EmbeddedDatabaseBuilder() + .generateUniqueName(true) + .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql") + .build(); + // @formatter:on + } + + @Bean + public DataSource additionalDataSource() { + return new EmbeddedDatabaseBuilder().generateUniqueName(true).build(); + } + + } + + + private JdbcTemplate jdbcTemplate; + + + @Autowired + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Test + @Sql("data.sql") + public void dataSourceTest() { + TransactionTestUtils.assertInTransaction(false); + assertEquals("Number of rows in the 'user' table.", 1, + JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user")); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java new file mode 100644 index 0000000000..d9cd152679 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java @@ -0,0 +1,122 @@ +/* + * Copyright 2002-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction; + +import javax.sql.DataSource; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.io.ClassPathResource; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.jdbc.JdbcTestUtils; +import org.springframework.test.transaction.TransactionTestUtils; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Transactional; + +import static org.junit.Assert.*; + +/** + * Integration tests that ensure that primary transaction managers + * are supported. + * + * @author Sam Brannen + * @since 4.3 + * @see org.springframework.test.context.jdbc.PrimaryDataSourceTests + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@DirtiesContext +public class PrimaryTransactionManagerTests { + + @Configuration + static class Config { + + @Primary + @Bean + public PlatformTransactionManager primaryTransactionManager() { + return new DataSourceTransactionManager(dataSource1()); + } + + @Bean + public PlatformTransactionManager additionalTransactionManager() { + return new DataSourceTransactionManager(dataSource2()); + } + + @Bean + public DataSource dataSource1() { + // @formatter:off + return new EmbeddedDatabaseBuilder() + .generateUniqueName(true) + .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql") + .build(); + // @formatter:on + } + + @Bean + public DataSource dataSource2() { + return new EmbeddedDatabaseBuilder().generateUniqueName(true).build(); + } + + } + + + private JdbcTemplate jdbcTemplate; + + + @Autowired + public void setDataSource(DataSource dataSource1) { + this.jdbcTemplate = new JdbcTemplate(dataSource1); + } + + @BeforeTransaction + public void beforeTransaction() { + assertNumUsers(0); + } + + @Test + @Transactional + public void transactionalTest() { + TransactionTestUtils.assertInTransaction(true); + + ClassPathResource resource = new ClassPathResource("/org/springframework/test/context/jdbc/data.sql"); + new ResourceDatabasePopulator(resource).execute(jdbcTemplate.getDataSource()); + + assertNumUsers(1); + } + + @AfterTransaction + public void afterTransaction() { + assertNumUsers(0); + } + + private void assertNumUsers(int expected) { + assertEquals("Number of rows in the 'user' table.", expected, + JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user")); + } + +} -- GitLab