001: /*
002: *
003: * The DbUnit Database Testing Framework
004: * Copyright (C)2002-2004, DbUnit.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: */
021:
022: package org.dbunit.operation;
023:
024: import org.slf4j.Logger;
025: import org.slf4j.LoggerFactory;
026:
027: import org.dbunit.DatabaseUnitException;
028: import org.dbunit.database.DatabaseConfig;
029: import org.dbunit.database.IDatabaseConnection;
030: import org.dbunit.database.statement.IPreparedBatchStatement;
031: import org.dbunit.database.statement.IStatementFactory;
032: import org.dbunit.dataset.Column;
033: import org.dbunit.dataset.DataSetException;
034: import org.dbunit.dataset.IDataSet;
035: import org.dbunit.dataset.ITable;
036: import org.dbunit.dataset.ITableIterator;
037: import org.dbunit.dataset.ITableMetaData;
038: import org.dbunit.dataset.RowOutOfBoundsException;
039: import org.dbunit.dataset.datatype.TypeCastException;
040:
041: import java.sql.SQLException;
042: import java.util.BitSet;
043:
044: /**
045: * Base implementation for database operation that are executed in batch.
046: *
047: * @author Manuel Laflamme
048: * @version $Revision: 558 $
049: * @since Feb 19, 2002
050: */
051: public abstract class AbstractBatchOperation extends AbstractOperation {
052:
053: /**
054: * Logger for this class
055: */
056: private static final Logger logger = LoggerFactory
057: .getLogger(AbstractBatchOperation.class);
058:
059: private static final BitSet EMPTY_BITSET = new BitSet();
060: protected boolean _reverseRowOrder = false;
061:
062: static boolean isEmpty(ITable table) throws DataSetException {
063: logger.debug("isEmpty(table=" + table + ") - start");
064:
065: Column[] columns = table.getTableMetaData().getColumns();
066:
067: // No columns = empty
068: if (columns.length == 0) {
069: return true;
070: }
071:
072: // Try to fetch first table value
073: try {
074: table.getValue(0, columns[0].getColumnName());
075: return false;
076: } catch (RowOutOfBoundsException e) {
077: logger.error("isEmpty()", e);
078:
079: // Not able to access first row thus empty
080: return true;
081: }
082: }
083:
084: /**
085: * Returns list of tables this operation is applied to. This method
086: * allow subclass to do filtering.
087: */
088: protected ITableIterator iterator(IDataSet dataSet)
089: throws DatabaseUnitException {
090: logger.debug("iterator(dataSet) - start");
091:
092: return dataSet.iterator();
093: }
094:
095: /**
096: * Returns mapping of columns to ignore by this operation. Each bit set represent
097: * a column to ignore.
098: */
099: BitSet getIgnoreMapping(ITable table, int row)
100: throws DataSetException {
101: logger.debug("getIgnoreMapping(table=" + table + ", row=" + row
102: + ") - start");
103:
104: return EMPTY_BITSET;
105: }
106:
107: /**
108: * Returns false if the specified table row have a different ignore mapping
109: * than the specified mapping.
110: */
111: boolean equalsIgnoreMapping(BitSet ignoreMapping, ITable table,
112: int row) throws DataSetException {
113: logger.debug("equalsIgnoreMapping(ignoreMapping="
114: + ignoreMapping + ", table=" + table + ", row=" + row
115: + ") - start");
116:
117: return true;
118: }
119:
120: abstract OperationData getOperationData(ITableMetaData metaData,
121: BitSet ignoreMapping, IDatabaseConnection connection)
122: throws DataSetException;
123:
124: ////////////////////////////////////////////////////////////////////////////
125: // DatabaseOperation class
126:
127: public void execute(IDatabaseConnection connection, IDataSet dataSet)
128: throws DatabaseUnitException, SQLException {
129: logger.debug("execute(connection=" + connection
130: + ", dataSet) - start");
131:
132: DatabaseConfig databaseConfig = connection.getConfig();
133: IStatementFactory factory = (IStatementFactory) databaseConfig
134: .getProperty(DatabaseConfig.PROPERTY_STATEMENT_FACTORY);
135:
136: // for each table
137: ITableIterator iterator = iterator(dataSet);
138: while (iterator.next()) {
139: ITable table = iterator.getTable();
140:
141: // Do not process empty table
142: if (isEmpty(table)) {
143: continue;
144: }
145:
146: ITableMetaData metaData = getOperationMetaData(connection,
147: table.getTableMetaData());
148: BitSet ignoreMapping = null;
149: OperationData operationData = null;
150: IPreparedBatchStatement statement = null;
151:
152: try {
153: // For each row
154: int start = _reverseRowOrder ? table.getRowCount() - 1
155: : 0;
156: int increment = _reverseRowOrder ? -1 : 1;
157:
158: try {
159: for (int i = start;; i = i + increment) {
160: int row = i;
161:
162: // If current row have a diffrent ignore value mapping than
163: // previous one, we generate a new statement
164: if (ignoreMapping == null
165: || !equalsIgnoreMapping(ignoreMapping,
166: table, row)) {
167: // Execute and close previous statement
168: if (statement != null) {
169: statement.executeBatch();
170: statement.clearBatch();
171: statement.close();
172: }
173:
174: ignoreMapping = getIgnoreMapping(table, row);
175: operationData = getOperationData(metaData,
176: ignoreMapping, connection);
177: statement = factory
178: .createPreparedBatchStatement(
179: operationData.getSql(),
180: connection);
181: }
182:
183: // for each column
184: Column[] columns = operationData.getColumns();
185: for (int j = 0; j < columns.length; j++) {
186: // Bind value only if not in ignore mapping
187: if (!ignoreMapping.get(j)) {
188: Column column = columns[j];
189: try {
190: statement.addValue(table
191: .getValue(row, column
192: .getColumnName()),
193: column.getDataType());
194: } catch (TypeCastException e) {
195: logger.error("execute()", e);
196:
197: throw new TypeCastException(
198: "Error casting value for table '"
199: + table
200: .getTableMetaData()
201: .getTableName()
202: + "' and column '"
203: + column
204: .getColumnName()
205: + "'", e);
206: }
207: }
208: }
209: statement.addBatch();
210: }
211: } catch (RowOutOfBoundsException e) {
212: logger.error("execute()", e);
213:
214: // end of table
215: }
216:
217: statement.executeBatch();
218: statement.clearBatch();
219: } finally {
220: if (statement != null) {
221: statement.close();
222: }
223: }
224: }
225: }
226: }
|