View Javadoc

1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2004, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  
22  package org.dbunit.operation;
23  
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  import org.dbunit.DatabaseUnitException;
28  import org.dbunit.database.DatabaseConfig;
29  import org.dbunit.database.IDatabaseConnection;
30  import org.dbunit.database.statement.IPreparedBatchStatement;
31  import org.dbunit.database.statement.IStatementFactory;
32  import org.dbunit.dataset.Column;
33  import org.dbunit.dataset.DataSetException;
34  import org.dbunit.dataset.IDataSet;
35  import org.dbunit.dataset.ITable;
36  import org.dbunit.dataset.ITableIterator;
37  import org.dbunit.dataset.ITableMetaData;
38  import org.dbunit.dataset.RowOutOfBoundsException;
39  import org.dbunit.dataset.datatype.TypeCastException;
40  
41  import java.sql.SQLException;
42  import java.util.BitSet;
43  
44  /**
45   * Base implementation for database operation that are executed in batch.
46   *
47   * @author Manuel Laflamme
48   * @version $Revision: 720 $
49   * @since Feb 19, 2002
50   */
51  public abstract class AbstractBatchOperation extends AbstractOperation
52  {
53  
54      /**
55       * Logger for this class
56       */
57      private static final Logger logger = LoggerFactory.getLogger(AbstractBatchOperation.class);
58  
59      private static final BitSet EMPTY_BITSET = new BitSet();
60      protected boolean _reverseRowOrder = false;
61  
62      static boolean isEmpty(ITable table) throws DataSetException
63      {
64          logger.debug("isEmpty(table={}) - start", table);
65  
66          Column[] columns = table.getTableMetaData().getColumns();
67  
68          // No columns = empty
69          if (columns.length == 0)
70          {
71              return true;
72          }
73  
74          // Try to fetch first table value
75          try
76          {
77              table.getValue(0, columns[0].getColumnName());
78              return false;
79          }
80          catch (RowOutOfBoundsException e)
81          {
82              // Not able to access first row thus empty
83              return true;
84          }
85      }
86  
87      /**
88       * Returns list of tables this operation is applied to. This method
89       * allow subclass to do filtering.
90       */
91      protected ITableIterator iterator(IDataSet dataSet) throws DatabaseUnitException
92      {
93          return dataSet.iterator();
94      }
95  
96      /**
97       * Returns mapping of columns to ignore by this operation. Each bit set represent
98       * a column to ignore.
99       */
100     BitSet getIgnoreMapping(ITable table, int row) throws DataSetException
101     {
102         return EMPTY_BITSET;
103     }
104 
105     /**
106      * Returns false if the specified table row have a different ignore mapping
107      * than the specified mapping.
108      */
109     boolean equalsIgnoreMapping(BitSet ignoreMapping, ITable table, int row) throws DataSetException
110     {
111         return true;
112     }
113 
114     abstract OperationData getOperationData(ITableMetaData metaData,
115             BitSet ignoreMapping, IDatabaseConnection connection) throws DataSetException;
116 
117     ////////////////////////////////////////////////////////////////////////////
118     // DatabaseOperation class
119 
120     public void execute(IDatabaseConnection connection, IDataSet dataSet)
121             throws DatabaseUnitException, SQLException
122     {
123         logger.debug("execute(connection={}, dataSet={}) - start", connection, dataSet);
124 
125         DatabaseConfig databaseConfig = connection.getConfig();
126         IStatementFactory factory = (IStatementFactory)databaseConfig.getProperty(DatabaseConfig.PROPERTY_STATEMENT_FACTORY);
127 
128         // for each table
129         ITableIterator iterator = iterator(dataSet);
130         while (iterator.next())
131         {
132             ITable table = iterator.getTable();
133 
134             // Do not process empty table
135             if (isEmpty(table))
136             {
137                 continue;
138             }
139 
140             ITableMetaData metaData = getOperationMetaData(connection, table.getTableMetaData());
141             BitSet ignoreMapping = null;
142             OperationData operationData = null;
143             IPreparedBatchStatement statement = null;
144 
145             try
146             {
147                 // For each row
148                 int start = _reverseRowOrder ? table.getRowCount() - 1 : 0;
149                 int increment = _reverseRowOrder ? -1 : 1;
150 
151                 try
152                 {
153                     for (int i = start; ; i = i + increment)
154                     {
155                         int row = i;
156 
157                         // If current row have a different ignore value mapping than
158                         // previous one, we generate a new statement
159                         if (ignoreMapping == null || !equalsIgnoreMapping(ignoreMapping, table, row))
160                         {
161                             // Execute and close previous statement
162                             if (statement != null)
163                             {
164                                 statement.executeBatch();
165                                 statement.clearBatch();
166                                 statement.close();
167                             }
168 
169                             ignoreMapping = getIgnoreMapping(table, row);
170                             operationData = getOperationData(metaData, ignoreMapping, connection);
171                             statement = factory.createPreparedBatchStatement(
172                                     operationData.getSql(), connection);
173                         }
174 
175 
176                         // for each column
177                         Column[] columns = operationData.getColumns();
178                         for (int j = 0; j < columns.length; j++)
179                         {
180                             // Bind value only if not in ignore mapping
181                             if (!ignoreMapping.get(j))
182                             {
183                                 Column column = columns[j];
184                             	try
185 								{
186 	                                statement.addValue(table.getValue(row, column.getColumnName()), column.getDataType());
187 								}
188                                 catch (TypeCastException e)
189 								{
190 				                	throw new TypeCastException("Error casting value for table '" + table.getTableMetaData().getTableName() 
191 				                			+"' and column '" + column.getColumnName() + "'", e);
192 								}
193                             }
194                         }
195                         statement.addBatch();
196                     }
197                 }
198                 catch (RowOutOfBoundsException e)
199                 {
200                 	// This exception occurs when records are exhausted
201                 	// and we reach the end of the table.  Ignore this error
202 
203                     // end of table
204                 }
205 
206                 statement.executeBatch();
207                 statement.clearBatch();
208             }
209             finally
210             {
211             	if (statement != null)
212                 {
213                     statement.close();
214                 }
215             }
216         }
217     }
218     
219     public String toString()
220     {
221     	StringBuffer sb = new StringBuffer();
222     	sb.append(getClass().getName()).append("[");
223     	sb.append("_reverseRowOrder=").append(this._reverseRowOrder);
224     	sb.append(", super=").append(super.toString());
225     	sb.append("]");
226     	return sb.toString();
227     }
228     
229 }