001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.test;
018:
019: import java.io.IOException;
020: import java.io.InputStreamReader;
021: import java.io.LineNumberReader;
022: import java.util.Iterator;
023: import java.util.LinkedList;
024: import java.util.List;
025:
026: import javax.sql.DataSource;
027:
028: import org.springframework.core.io.Resource;
029: import org.springframework.dao.DataAccessException;
030: import org.springframework.dao.DataAccessResourceFailureException;
031: import org.springframework.jdbc.core.JdbcTemplate;
032: import org.springframework.util.StringUtils;
033:
034: /**
035: * Subclass of AbstractTransactionalSpringContextTests that adds some convenience
036: * functionality for JDBC access. Expects a {@link javax.sql.DataSource} bean
037: * to be defined in the Spring application context.
038: *
039: * <p>This class exposes a {@link org.springframework.jdbc.core.JdbcTemplate}
040: * and provides an easy way to delete from the database in a new transaction.
041: *
042: * @author Rod Johnson
043: * @author Juergen Hoeller
044: * @since 1.1.1
045: * @see #setDataSource(javax.sql.DataSource)
046: * @see #getJdbcTemplate()
047: */
048: public abstract class AbstractTransactionalDataSourceSpringContextTests
049: extends AbstractTransactionalSpringContextTests {
050:
051: protected JdbcTemplate jdbcTemplate;
052:
053: /**
054: * Did this test delete any tables? If so, we forbid transaction completion,
055: * and only allow rollback.
056: */
057: private boolean zappedTables;
058:
059: /**
060: * Default constructor for AbstractTransactionalDataSourceSpringContextTests.
061: */
062: public AbstractTransactionalDataSourceSpringContextTests() {
063: }
064:
065: /**
066: * Constructor for AbstractTransactionalDataSourceSpringContextTests with a JUnit name.
067: */
068: public AbstractTransactionalDataSourceSpringContextTests(String name) {
069: super (name);
070: }
071:
072: /**
073: * Setter: DataSource is provided by Dependency Injection.
074: */
075: public void setDataSource(DataSource dataSource) {
076: this .jdbcTemplate = new JdbcTemplate(dataSource);
077: }
078:
079: /**
080: * Return the JdbcTemplate that this base class manages.
081: */
082: public final JdbcTemplate getJdbcTemplate() {
083: return this .jdbcTemplate;
084: }
085:
086: /**
087: * Convenient method to delete all rows from these tables.
088: * Calling this method will make avoidance of rollback by calling
089: * <code>setComplete()</code> impossible.
090: * @see #setComplete
091: */
092: protected void deleteFromTables(String[] names) {
093: for (int i = 0; i < names.length; i++) {
094: int rowCount = this .jdbcTemplate.update("DELETE FROM "
095: + names[i]);
096: if (logger.isInfoEnabled()) {
097: logger.info("Deleted " + rowCount + " rows from table "
098: + names[i]);
099: }
100: }
101: this .zappedTables = true;
102: }
103:
104: /**
105: * Overridden to prevent the transaction committing if a number of tables have been
106: * cleared, as a defensive measure against accidental <i>permanent</i> wiping of a database.
107: * @see org.springframework.test.AbstractTransactionalSpringContextTests#setComplete()
108: */
109: protected final void setComplete() {
110: if (this .zappedTables) {
111: throw new IllegalStateException(
112: "Cannot set complete after deleting tables");
113: }
114: super .setComplete();
115: }
116:
117: /**
118: * Count the rows in the given table
119: * @param tableName table name to count rows in
120: * @return the number of rows in the table
121: */
122: protected int countRowsInTable(String tableName) {
123: return this .jdbcTemplate.queryForInt("SELECT COUNT(0) FROM "
124: + tableName);
125: }
126:
127: /**
128: * Execute the given SQL script. Will be rolled back by default,
129: * according to the fate of the current transaction.
130: * @param sqlResourcePath Spring resource path for the SQL script.
131: * Should normally be loaded by classpath. There should be one statement
132: * per line. Any semicolons will be removed.
133: * <b>Do not use this method to execute DDL if you expect rollback.</b>
134: * @param continueOnError whether or not to continue without throwing
135: * an exception in the event of an error
136: * @throws DataAccessException if there is an error executing a statement
137: * and continueOnError was false
138: */
139: protected void executeSqlScript(String sqlResourcePath,
140: boolean continueOnError) throws DataAccessException {
141: if (logger.isInfoEnabled()) {
142: logger.info("Executing SQL script '" + sqlResourcePath
143: + "'");
144: }
145:
146: long startTime = System.currentTimeMillis();
147: List statements = new LinkedList();
148: Resource res = getApplicationContext().getResource(
149: sqlResourcePath);
150: try {
151: LineNumberReader lnr = new LineNumberReader(
152: new InputStreamReader(res.getInputStream()));
153: String currentStatement = lnr.readLine();
154: while (currentStatement != null) {
155: currentStatement = StringUtils.replace(
156: currentStatement, ";", "");
157: statements.add(currentStatement);
158: currentStatement = lnr.readLine();
159: }
160:
161: for (Iterator itr = statements.iterator(); itr.hasNext();) {
162: String statement = (String) itr.next();
163: try {
164: int rowsAffected = this .jdbcTemplate
165: .update(statement);
166: if (logger.isDebugEnabled()) {
167: logger
168: .debug(rowsAffected
169: + " rows affected by SQL: "
170: + statement);
171: }
172: } catch (DataAccessException ex) {
173: if (continueOnError) {
174: if (logger.isWarnEnabled()) {
175: logger
176: .warn("SQL: " + statement
177: + " failed", ex);
178: }
179: } else {
180: throw ex;
181: }
182: }
183: }
184: long elapsedTime = System.currentTimeMillis() - startTime;
185: logger.info("Done executing SQL script '" + sqlResourcePath
186: + "' in " + elapsedTime + " ms");
187: } catch (IOException ex) {
188: throw new DataAccessResourceFailureException(
189: "Failed to open SQL script '" + sqlResourcePath
190: + "'", ex);
191: }
192: }
193:
194: }
|