001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.test.context.junit4; 018 019import javax.sql.DataSource; 020 021import org.springframework.beans.factory.annotation.Autowired; 022import org.springframework.context.ApplicationContext; 023import org.springframework.core.io.Resource; 024import org.springframework.dao.DataAccessException; 025import org.springframework.jdbc.core.JdbcTemplate; 026import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; 027import org.springframework.lang.Nullable; 028import org.springframework.test.context.ContextConfiguration; 029import org.springframework.test.context.TestExecutionListeners; 030import org.springframework.test.context.event.EventPublishingTestExecutionListener; 031import org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener; 032import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; 033import org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener; 034import org.springframework.test.context.support.DirtiesContextTestExecutionListener; 035import org.springframework.test.context.transaction.TransactionalTestExecutionListener; 036import org.springframework.test.context.web.ServletTestExecutionListener; 037import org.springframework.test.jdbc.JdbcTestUtils; 038import org.springframework.transaction.PlatformTransactionManager; 039import org.springframework.transaction.annotation.Transactional; 040import org.springframework.util.Assert; 041 042/** 043 * Abstract {@linkplain Transactional transactional} extension of 044 * {@link AbstractJUnit4SpringContextTests} which adds convenience functionality 045 * for JDBC access. Expects a {@link DataSource} bean and a 046 * {@link PlatformTransactionManager} bean to be defined in the Spring 047 * {@linkplain ApplicationContext application context}. 048 * 049 * <p>This class exposes a {@link JdbcTemplate} and provides an easy way to 050 * {@linkplain #countRowsInTable count the number of rows in a table} 051 * (potentially {@linkplain #countRowsInTableWhere with a WHERE clause}), 052 * {@linkplain #deleteFromTables delete from tables}, 053 * {@linkplain #dropTables drop tables}, and 054 * {@linkplain #executeSqlScript execute SQL scripts} within a transaction. 055 * 056 * <p>Concrete subclasses must fulfill the same requirements outlined in 057 * {@link AbstractJUnit4SpringContextTests}. 058 * 059 * <p>The following {@link org.springframework.test.context.TestExecutionListener 060 * TestExecutionListeners} are configured by default: 061 * 062 * <ul> 063 * <li>{@link org.springframework.test.context.web.ServletTestExecutionListener} 064 * <li>{@link org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener} 065 * <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener} 066 * <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener} 067 * <li>{@link org.springframework.test.context.transaction.TransactionalTestExecutionListener} 068 * <li>{@link org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener} 069 * <li>{@link org.springframework.test.context.event.EventPublishingTestExecutionListener} 070 * </ul> 071 * 072 * <p>This class serves only as a convenience for extension. 073 * <ul> 074 * <li>If you do not wish for your test classes to be tied to a Spring-specific 075 * class hierarchy, you may configure your own custom test classes by using 076 * {@link SpringRunner}, {@link ContextConfiguration @ContextConfiguration}, 077 * {@link TestExecutionListeners @TestExecutionListeners}, etc.</li> 078 * <li>If you wish to extend this class and use a runner other than the 079 * {@link SpringRunner}, as of Spring Framework 4.2 you can use 080 * {@link org.springframework.test.context.junit4.rules.SpringClassRule SpringClassRule} and 081 * {@link org.springframework.test.context.junit4.rules.SpringMethodRule SpringMethodRule} 082 * and specify your runner of choice via {@link org.junit.runner.RunWith @RunWith(...)}.</li> 083 * </ul> 084 * 085 * <p><strong>NOTE:</strong> This class requires JUnit 4.12 or higher. 086 * 087 * @author Sam Brannen 088 * @author Juergen Hoeller 089 * @since 2.5 090 * @see AbstractJUnit4SpringContextTests 091 * @see org.springframework.test.context.ContextConfiguration 092 * @see org.springframework.test.context.TestExecutionListeners 093 * @see org.springframework.test.context.transaction.TransactionalTestExecutionListener 094 * @see org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener 095 * @see org.springframework.transaction.annotation.Transactional 096 * @see org.springframework.test.annotation.Commit 097 * @see org.springframework.test.annotation.Rollback 098 * @see org.springframework.test.context.transaction.BeforeTransaction 099 * @see org.springframework.test.context.transaction.AfterTransaction 100 * @see org.springframework.test.jdbc.JdbcTestUtils 101 * @see org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests 102 */ 103@TestExecutionListeners(listeners = { ServletTestExecutionListener.class, 104 DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, 105 DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, 106 SqlScriptsTestExecutionListener.class, EventPublishingTestExecutionListener.class }, inheritListeners = false) 107@Transactional 108public abstract class AbstractTransactionalJUnit4SpringContextTests extends AbstractJUnit4SpringContextTests { 109 110 /** 111 * The {@code JdbcTemplate} that this base class manages, available to subclasses. 112 * @since 3.2 113 */ 114 protected final JdbcTemplate jdbcTemplate = new JdbcTemplate(); 115 116 @Nullable 117 private String sqlScriptEncoding; 118 119 120 /** 121 * Set the {@code DataSource}, typically provided via Dependency Injection. 122 * <p>This method also instantiates the {@link #jdbcTemplate} instance variable. 123 */ 124 @Autowired 125 public void setDataSource(DataSource dataSource) { 126 this.jdbcTemplate.setDataSource(dataSource); 127 } 128 129 /** 130 * Specify the encoding for SQL scripts, if different from the platform encoding. 131 * @see #executeSqlScript 132 */ 133 public void setSqlScriptEncoding(String sqlScriptEncoding) { 134 this.sqlScriptEncoding = sqlScriptEncoding; 135 } 136 137 /** 138 * Convenience method for counting the rows in the given table. 139 * @param tableName table name to count rows in 140 * @return the number of rows in the table 141 * @see JdbcTestUtils#countRowsInTable 142 */ 143 protected int countRowsInTable(String tableName) { 144 return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); 145 } 146 147 /** 148 * Convenience method for counting the rows in the given table, using the 149 * provided {@code WHERE} clause. 150 * <p>See the Javadoc for {@link JdbcTestUtils#countRowsInTableWhere} for details. 151 * @param tableName the name of the table to count rows in 152 * @param whereClause the {@code WHERE} clause to append to the query 153 * @return the number of rows in the table that match the provided 154 * {@code WHERE} clause 155 * @since 3.2 156 * @see JdbcTestUtils#countRowsInTableWhere 157 */ 158 protected int countRowsInTableWhere(String tableName, String whereClause) { 159 return JdbcTestUtils.countRowsInTableWhere(this.jdbcTemplate, tableName, whereClause); 160 } 161 162 /** 163 * Convenience method for deleting all rows from the specified tables. 164 * <p>Use with caution outside of a transaction! 165 * @param names the names of the tables from which to delete 166 * @return the total number of rows deleted from all specified tables 167 * @see JdbcTestUtils#deleteFromTables 168 */ 169 protected int deleteFromTables(String... names) { 170 return JdbcTestUtils.deleteFromTables(this.jdbcTemplate, names); 171 } 172 173 /** 174 * Convenience method for deleting all rows from the given table, using the 175 * provided {@code WHERE} clause. 176 * <p>Use with caution outside of a transaction! 177 * <p>See the Javadoc for {@link JdbcTestUtils#deleteFromTableWhere} for details. 178 * @param tableName the name of the table to delete rows from 179 * @param whereClause the {@code WHERE} clause to append to the query 180 * @param args arguments to bind to the query (leaving it to the {@code 181 * PreparedStatement} to guess the corresponding SQL type); may also contain 182 * {@link org.springframework.jdbc.core.SqlParameterValue SqlParameterValue} 183 * objects which indicate not only the argument value but also the SQL type 184 * and optionally the scale. 185 * @return the number of rows deleted from the table 186 * @since 4.0 187 * @see JdbcTestUtils#deleteFromTableWhere 188 */ 189 protected int deleteFromTableWhere(String tableName, String whereClause, Object... args) { 190 return JdbcTestUtils.deleteFromTableWhere(this.jdbcTemplate, tableName, whereClause, args); 191 } 192 193 /** 194 * Convenience method for dropping all of the specified tables. 195 * <p>Use with caution outside of a transaction! 196 * @param names the names of the tables to drop 197 * @since 3.2 198 * @see JdbcTestUtils#dropTables 199 */ 200 protected void dropTables(String... names) { 201 JdbcTestUtils.dropTables(this.jdbcTemplate, names); 202 } 203 204 /** 205 * Execute the given SQL script. 206 * <p>Use with caution outside of a transaction! 207 * <p>The script will normally be loaded by classpath. 208 * <p><b>Do not use this method to execute DDL if you expect rollback.</b> 209 * @param sqlResourcePath the Spring resource path for the SQL script 210 * @param continueOnError whether or not to continue without throwing an 211 * exception in the event of an error 212 * @throws DataAccessException if there is an error executing a statement 213 * @see ResourceDatabasePopulator 214 * @see #setSqlScriptEncoding 215 */ 216 protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException { 217 DataSource ds = this.jdbcTemplate.getDataSource(); 218 Assert.state(ds != null, "No DataSource set"); 219 Assert.state(this.applicationContext != null, "No ApplicationContext set"); 220 Resource resource = this.applicationContext.getResource(sqlResourcePath); 221 new ResourceDatabasePopulator(continueOnError, false, this.sqlScriptEncoding, resource).execute(ds); 222 } 223 224}