1 package org.apache.torque.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.Serializable;
23 import java.sql.Connection;
24 import java.sql.PreparedStatement;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import org.apache.commons.lang.StringUtils;
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.torque.Database;
39 import org.apache.torque.TooManyRowsException;
40 import org.apache.torque.Torque;
41 import org.apache.torque.TorqueException;
42 import org.apache.torque.adapter.DB;
43 import org.apache.torque.map.ColumnMap;
44 import org.apache.torque.map.DatabaseMap;
45 import org.apache.torque.map.MapBuilder;
46 import org.apache.torque.map.TableMap;
47 import org.apache.torque.oid.IdGenerator;
48 import org.apache.torque.om.NumberKey;
49 import org.apache.torque.om.ObjectKey;
50 import org.apache.torque.om.SimpleKey;
51 import org.apache.torque.om.StringKey;
52
53 import com.workingdogs.village.Column;
54 import com.workingdogs.village.DataSet;
55 import com.workingdogs.village.DataSetException;
56 import com.workingdogs.village.KeyDef;
57 import com.workingdogs.village.QueryDataSet;
58 import com.workingdogs.village.Record;
59 import com.workingdogs.village.Schema;
60 import com.workingdogs.village.TableDataSet;
61
62 /***
63 * This is the base class for all Peer classes in the system. Peer
64 * classes are responsible for isolating all of the database access
65 * for a specific business object. They execute all of the SQL
66 * against the database. Over time this class has grown to include
67 * utility methods which ease execution of cross-database queries and
68 * the implementation of concrete Peers.
69 *
70 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
71 * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
72 * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
73 * @author <a href="mailto:stephenh@chase3000.com">Stephen Haberman</a>
74 * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
75 * @author <a href="mailto:vido@ldh.org">Augustin Vidovic</a>
76 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
77 * @version $Id: BasePeer.java 750198 2009-03-04 22:34:43Z gmonroe $
78 */
79 public abstract class BasePeer
80 implements Serializable
81 {
82 /*** Constant criteria key to reference ORDER BY columns. */
83 public static final String ORDER_BY = "ORDER BY";
84
85 /***
86 * Constant criteria key to remove Case Information from
87 * search/ordering criteria.
88 */
89 public static final String IGNORE_CASE = "IgNOrE cAsE";
90
91 /*** Classes that implement this class should override this value. */
92 public static final String TABLE_NAME = "TABLE_NAME";
93
94 /*** the log */
95 protected static final Log log = LogFactory.getLog(BasePeer.class);
96
97 private static void throwTorqueException(Exception e)
98 throws TorqueException
99 {
100 if (e instanceof TorqueException)
101 {
102 throw (TorqueException) e;
103 }
104 else
105 {
106 throw new TorqueException(e);
107 }
108 }
109
110 /***
111 * Sets up a Schema for a table. This schema is then normally
112 * used as the argument for initTableColumns().
113 *
114 * @param tableName The name of the table.
115 * @return A Schema.
116 */
117 public static Schema initTableSchema(String tableName)
118 {
119 return initTableSchema(tableName, Torque.getDefaultDB());
120 }
121
122 /***
123 * Sets up a Schema for a table. This schema is then normally
124 * used as the argument for initTableColumns
125 *
126 * @param tableName The propery name for the database in the
127 * configuration file.
128 * @param dbName The name of the database.
129 * @return A Schema.
130 */
131 public static Schema initTableSchema(String tableName, String dbName)
132 {
133 Schema schema = null;
134 Connection con = null;
135
136 try
137 {
138 con = Torque.getConnection(dbName);
139 schema = new Schema().schema(con, tableName);
140 }
141 catch (Exception e)
142 {
143 log.error(e);
144 throw new Error("Error in BasePeer.initTableSchema("
145 + tableName
146 + "): "
147 + e.getMessage());
148 }
149 finally
150 {
151 Torque.closeConnection(con);
152 }
153 return schema;
154 }
155
156 /***
157 * Creates a Column array for a table based on its Schema.
158 *
159 * @param schema A Schema object.
160 * @return A Column[].
161 */
162 public static Column[] initTableColumns(Schema schema)
163 {
164 Column[] columns = null;
165 try
166 {
167 int numberOfColumns = schema.numberOfColumns();
168 columns = new Column[numberOfColumns];
169 for (int i = 0; i < numberOfColumns; i++)
170 {
171 columns[i] = schema.column(i + 1);
172 }
173 }
174 catch (Exception e)
175 {
176 log.error(e);
177 throw new Error(
178 "Error in BasePeer.initTableColumns(): " + e.getMessage());
179 }
180 return columns;
181 }
182
183 /***
184 * Convenience method to create a String array of column names.
185 *
186 * @param columns A Column[].
187 * @return A String[].
188 */
189 public static String[] initColumnNames(Column[] columns)
190 {
191 String[] columnNames = new String[columns.length];
192 for (int i = 0; i < columns.length; i++)
193 {
194 columnNames[i] = columns[i].name().toUpperCase();
195 }
196 return columnNames;
197 }
198
199 /***
200 * Convenience method to create a String array of criteria keys.
201 *
202 * @param tableName Name of table.
203 * @param columnNames A String[].
204 * @return A String[].
205 */
206 public static String[] initCriteriaKeys(
207 String tableName,
208 String[] columnNames)
209 {
210 String[] keys = new String[columnNames.length];
211 for (int i = 0; i < columnNames.length; i++)
212 {
213 keys[i] = tableName + "." + columnNames[i].toUpperCase();
214 }
215 return keys;
216 }
217
218 /***
219 * Convenience method that uses straight JDBC to delete multiple
220 * rows. Village throws an Exception when multiple rows are
221 * deleted.
222 *
223 * @param con A Connection.
224 * @param table The table to delete records from.
225 * @param column The column in the where clause.
226 * @param value The value of the column.
227 * @throws TorqueException Any exceptions caught during processing will be
228 * rethrown wrapped into a TorqueException.
229 */
230 public static void deleteAll(
231 Connection con,
232 String table,
233 String column,
234 int value)
235 throws TorqueException
236 {
237 Statement statement = null;
238 try
239 {
240 statement = con.createStatement();
241
242 StringBuffer query = new StringBuffer();
243 query.append("DELETE FROM ")
244 .append(table)
245 .append(" WHERE ")
246 .append(column)
247 .append(" = ")
248 .append(value);
249
250 statement.executeUpdate(query.toString());
251 }
252 catch (SQLException e)
253 {
254 throw new TorqueException(e);
255 }
256 finally
257 {
258 if (statement != null)
259 {
260 try
261 {
262 statement.close();
263 }
264 catch (SQLException e)
265 {
266 throw new TorqueException(e);
267 }
268 }
269 }
270 }
271
272 /***
273 * Convenience method that uses straight JDBC to delete multiple
274 * rows. Village throws an Exception when multiple rows are
275 * deleted. This method attempts to get the default database from
276 * the pool.
277 *
278 * @param table The table to delete records from.
279 * @param column The column in the where clause.
280 * @param value The value of the column.
281 * @throws TorqueException Any exceptions caught during processing will be
282 * rethrown wrapped into a TorqueException.
283 */
284 public static void deleteAll(String table, String column, int value)
285 throws TorqueException
286 {
287 Connection con = null;
288 try
289 {
290
291 con = Torque.getConnection(Torque.getDefaultDB());
292 deleteAll(con, table, column, value);
293 }
294 finally
295 {
296 Torque.closeConnection(con);
297 }
298 }
299
300 /***
301 * Method to perform deletes based on values and keys in a
302 * Criteria.
303 *
304 * @param criteria The criteria to use.
305 * @throws TorqueException Any exceptions caught during processing will be
306 * rethrown wrapped into a TorqueException.
307 * @deprecated This method causes unexpected results when joins are used.
308 * Please use doDelete(Criteria, String).
309 */
310 public static void doDelete(Criteria criteria) throws TorqueException
311 {
312 doDelete(criteria, (String) null);
313 }
314
315 /***
316 * Method to perform deletes based on values and keys in a
317 * Criteria.
318 * This method is protected because it may cause ambiguity between
319 * doDelete(Criteria,Connection) and this method. It will be made public
320 * once doDelete(Criteria, Connection) is removed.
321 *
322 * @param criteria The criteria to use.
323 * @param tableName the name of the table to delete records from.
324 * If set to null, the name of the table(s) can be extracted from
325 * the criteria, but this can cause unexpected results.
326 * @throws TorqueException Any exceptions caught during processing will be
327 * rethrown wrapped into a TorqueException.
328 */
329 protected static void doDelete(Criteria criteria, String tableName) throws TorqueException
330 {
331 Connection con = null;
332 try
333 {
334 con = Transaction.beginOptional(
335 criteria.getDbName(),
336 criteria.isUseTransaction());
337 doDelete(criteria, tableName, con);
338 Transaction.commit(con);
339 }
340 catch (TorqueException e)
341 {
342 Transaction.safeRollback(con);
343 throw e;
344 }
345 }
346
347 /***
348 * Method to perform deletes based on values and keys in a Criteria.
349 *
350 * @param criteria The criteria to use.
351 * @param con A Connection.
352 * @throws TorqueException Any exceptions caught during processing will be
353 * rethrown wrapped into a TorqueException.
354 * @deprecated This method causes unexpected results when joins are used.
355 * Please use doDelete(Criteria, String, Connection).
356 */
357 public static void doDelete(Criteria criteria, Connection con)
358 throws TorqueException
359 {
360 doDelete(criteria, null, con);
361 }
362
363 /***
364 * Method to perform deletes based on values and keys in a Criteria.
365 *
366 * @param criteria The criteria to use.
367 * @param tableName the name of the table to delete records from.
368 * If set to null, the name of the table(s) can be extracted from
369 * the criteria, but this can cause unexpected results.
370 * @param con A Connection.
371 * @throws TorqueException Any exceptions caught during processing will be
372 * rethrown wrapped into a TorqueException.
373 */
374 public static void doDelete(Criteria criteria, String tableName, Connection con)
375 throws TorqueException
376 {
377 String dbName = criteria.getDbName();
378 final DatabaseMap dbMap = Torque.getDatabaseMap(dbName);
379
380
381
382
383
384 SQLBuilder.TableCallback tc = new SQLBuilder.TableCallback() {
385 public void process (Set tables, String key, Criteria crit)
386 {
387 if (crit.isCascade())
388 {
389
390 TableMap[] tableMaps = dbMap.getTables();
391 for (int i = 0; i < tableMaps.length; i++)
392 {
393 ColumnMap[] columnMaps = tableMaps[i].getColumns();
394
395 for (int j = 0; j < columnMaps.length; j++)
396 {
397
398
399
400 if (columnMaps[j].isForeignKey()
401 && columnMaps[j].isPrimaryKey()
402 && key.equals(columnMaps[j].getRelatedName()))
403 {
404 tables.add(tableMaps[i].getName());
405 crit.add(columnMaps[j].getFullyQualifiedName(),
406 crit.getValue(key));
407 }
408 }
409 }
410 }
411 }
412 };
413
414 Set tables;
415 if (tableName == null)
416 {
417 tables = SQLBuilder.getTableSet(criteria, tc);
418 }
419 else
420 {
421 tables = new HashSet(1);
422 tables.add(tableName);
423 }
424
425 try
426 {
427 processTables(criteria, tables, con, new ProcessCallback() {
428 public void process(String table, String dbName, Record rec)
429 throws Exception
430 {
431 rec.markToBeDeleted();
432 rec.save();
433 }
434 });
435 }
436 catch (Exception e)
437 {
438 throwTorqueException(e);
439 }
440 }
441
442 /***
443 * Method to perform inserts based on values and keys in a
444 * Criteria.
445 * <p>
446 * If the primary key is auto incremented the data in Criteria
447 * will be inserted and the auto increment value will be returned.
448 * <p>
449 * If the primary key is included in Criteria then that value will
450 * be used to insert the row.
451 * <p>
452 * If no primary key is included in Criteria then we will try to
453 * figure out the primary key from the database map and insert the
454 * row with the next available id using util.db.IDBroker.
455 * <p>
456 * If no primary key is defined for the table the values will be
457 * inserted as specified in Criteria and -1 will be returned.
458 *
459 * @param criteria Object containing values to insert.
460 * @return An Object which is the id of the row that was inserted
461 * (if the table has a primary key) or null (if the table does not
462 * have a primary key).
463 * @throws TorqueException Any exceptions caught during processing will be
464 * rethrown wrapped into a TorqueException.
465 */
466 public static ObjectKey doInsert(Criteria criteria) throws TorqueException
467 {
468 Connection con = null;
469 ObjectKey id = null;
470
471 try
472 {
473 con = Transaction.beginOptional(
474 criteria.getDbName(),
475 criteria.isUseTransaction());
476 id = doInsert(criteria, con);
477 Transaction.commit(con);
478 }
479 catch (TorqueException e)
480 {
481 Transaction.safeRollback(con);
482 throw e;
483 }
484
485 return id;
486 }
487
488 /***
489 * Method to perform inserts based on values and keys in a
490 * Criteria.
491 * <p>
492 * If the primary key is auto incremented the data in Criteria
493 * will be inserted and the auto increment value will be returned.
494 * <p>
495 * If the primary key is included in Criteria then that value will
496 * be used to insert the row.
497 * <p>
498 * If no primary key is included in Criteria then we will try to
499 * figure out the primary key from the database map and insert the
500 * row with the next available id using util.db.IDBroker.
501 * <p>
502 * If no primary key is defined for the table the values will be
503 * inserted as specified in Criteria and null will be returned.
504 *
505 * @param criteria Object containing values to insert.
506 * @param con A Connection.
507 * @return An Object which is the id of the row that was inserted
508 * (if the table has a primary key) or null (if the table does not
509 * have a primary key).
510 * @throws TorqueException Any exceptions caught during processing will be
511 * rethrown wrapped into a TorqueException.
512 */
513 public static ObjectKey doInsert(Criteria criteria, Connection con)
514 throws TorqueException
515 {
516 SimpleKey id = null;
517
518
519
520 String table = null;
521 Iterator keys = criteria.keySet().iterator();
522 if (keys.hasNext())
523 {
524 table = criteria.getTableName((String) keys.next());
525 }
526 else
527 {
528 throw new TorqueException("Database insert attempted without "
529 + "anything specified to insert");
530 }
531
532 String dbName = criteria.getDbName();
533 Database database = Torque.getDatabase(dbName);
534 DatabaseMap dbMap = database.getDatabaseMap();
535 TableMap tableMap = dbMap.getTable(table);
536 Object keyInfo = tableMap.getPrimaryKeyMethodInfo();
537 IdGenerator keyGen
538 = database.getIdGenerator(tableMap.getPrimaryKeyMethod());
539
540 ColumnMap pk = getPrimaryKey(criteria);
541
542
543
544 if (keyGen != null && keyGen.isPriorToInsert())
545 {
546
547
548 if (pk != null && !criteria.containsKey(pk.getFullyQualifiedName()))
549 {
550 id = getId(pk, keyGen, con, keyInfo);
551 criteria.add(pk.getFullyQualifiedName(), id);
552 }
553 }
554
555
556 TableDataSet tds = null;
557 try
558 {
559 String tableName = SQLBuilder.getFullTableName(table, dbName);
560 tds = new TableDataSet(con, tableName);
561 Record rec = tds.addRecord();
562
563 BasePeer.insertOrUpdateRecord(rec, table, dbName, criteria);
564 }
565 catch (DataSetException e)
566 {
567 throwTorqueException(e);
568 }
569 catch (SQLException e)
570 {
571 throwTorqueException(e);
572 }
573 catch (TorqueException e)
574 {
575 throwTorqueException(e);
576 }
577 finally
578 {
579 VillageUtils.close(tds);
580 }
581
582
583
584 if (keyGen != null && keyGen.isPostInsert())
585 {
586 id = getId(pk, keyGen, con, keyInfo);
587 }
588
589 return id;
590 }
591
592 /***
593 * Create an Id for insertion in the Criteria
594 *
595 * @param pk ColumnMap for the Primary key
596 * @param keyGen The Id Generator object
597 * @param con The SQL Connection to run the id generation under
598 * @param keyInfo KeyInfo Parameter from the Table map
599 *
600 * @return A simple Key representing the new Id value
601 * @throws TorqueException Possible errors get wrapped in here.
602 */
603 private static SimpleKey getId(ColumnMap pk, IdGenerator keyGen, Connection con, Object keyInfo)
604 throws TorqueException
605 {
606 SimpleKey id = null;
607
608 try
609 {
610 if (pk != null && keyGen != null)
611 {
612 if (pk.getType() instanceof Number)
613 {
614 id = new NumberKey(
615 keyGen.getIdAsBigDecimal(con, keyInfo));
616 }
617 else
618 {
619 id = new StringKey(keyGen.getIdAsString(con, keyInfo));
620 }
621 }
622 }
623 catch (Exception e)
624 {
625 throwTorqueException(e);
626 }
627 return id;
628 }
629
630 /***
631 * Grouping of code used in both doInsert() and doUpdate()
632 * methods. Sets up a Record for saving.
633 *
634 * @param rec A Record.
635 * @param table Name of table.
636 * @param criteria A Criteria.
637 * @throws TorqueException Any exceptions caught during processing will be
638 * rethrown wrapped into a TorqueException.
639 */
640 private static void insertOrUpdateRecord(
641 Record rec,
642 String table,
643 String dbName,
644 Criteria criteria)
645 throws TorqueException
646 {
647 DatabaseMap dbMap = Torque.getDatabaseMap(dbName);
648
649 ColumnMap[] columnMaps = dbMap.getTable(table).getColumns();
650 boolean shouldSave = false;
651 for (int j = 0; j < columnMaps.length; j++)
652 {
653 ColumnMap colMap = columnMaps[j];
654 String colName = colMap.getColumnName();
655 String key = new StringBuffer(colMap.getTableName())
656 .append('.')
657 .append(colName)
658 .toString();
659 if (criteria.containsKey(key))
660 {
661 try
662 {
663 VillageUtils.setVillageValue(criteria, key, rec, colName);
664 shouldSave = true;
665 }
666 catch (Exception e)
667 {
668 throwTorqueException(e);
669 }
670 }
671 }
672
673 if (shouldSave)
674 {
675 try
676 {
677 rec.save();
678 }
679 catch (Exception e)
680 {
681 throwTorqueException(e);
682 }
683 }
684 else
685 {
686 throw new TorqueException("No changes to save");
687 }
688 }
689
690 /***
691 * Method to create an SQL query for display only based on values in a
692 * Criteria.
693 *
694 * @param criteria A Criteria.
695 * @return the SQL query for display
696 * @exception TorqueException Trouble creating the query string.
697 */
698 static String createQueryDisplayString(Criteria criteria)
699 throws TorqueException
700 {
701 return createQuery(criteria).toString();
702 }
703
704 /***
705 * Method to create an SQL query for actual execution based on values in a
706 * Criteria.
707 *
708 * @param criteria A Criteria.
709 * @return the SQL query for actual execution
710 * @exception TorqueException Trouble creating the query string.
711 */
712 public static String createQueryString(Criteria criteria)
713 throws TorqueException
714 {
715 Query query = createQuery(criteria);
716 return query.toString();
717 }
718
719 /***
720 * Method to create an SQL query based on values in a Criteria. Note that
721 * final manipulation of the limit and offset are performed when the query
722 * is actually executed.
723 *
724 * @param criteria A Criteria.
725 * @return the sql query
726 * @exception TorqueException Trouble creating the query string.
727 */
728 static Query createQuery(Criteria criteria)
729 throws TorqueException
730 {
731 return SQLBuilder.buildQueryClause(criteria, null, new SQLBuilder.QueryCallback() {
732 public String process(Criteria.Criterion criterion, List params)
733 {
734 return criterion.toString();
735 }
736 });
737 }
738
739 /***
740 * Returns all results.
741 *
742 * @param criteria A Criteria.
743 * @return List of Record objects.
744 * @throws TorqueException Any exceptions caught during processing will be
745 * rethrown wrapped into a TorqueException.
746 */
747 public static List doSelect(Criteria criteria) throws TorqueException
748 {
749 Connection con = null;
750 List results = null;
751
752 try
753 {
754 con = Transaction.beginOptional(
755 criteria.getDbName(),
756 criteria.isUseTransaction());
757 results = doSelect(criteria, con);
758 Transaction.commit(con);
759 }
760 catch (TorqueException e)
761 {
762 Transaction.safeRollback(con);
763 throw e;
764 }
765 return results;
766 }
767
768 /***
769 * Returns all results.
770 *
771 * @param criteria A Criteria.
772 * @param con A Connection.
773 * @return List of Record objects.
774 * @throws TorqueException Any exceptions caught during processing will be
775 * rethrown wrapped into a TorqueException.
776 */
777 public static List doSelect(Criteria criteria, Connection con)
778 throws TorqueException
779 {
780 Query query = createQuery(criteria);
781 DB dbadapter = Torque.getDB(criteria.getDbName());
782
783
784 return executeQuery(query.toString(),
785 dbadapter.supportsNativeOffset() ? 0 : criteria.getOffset(),
786 dbadapter.supportsNativeLimit() ? -1 : criteria.getLimit(),
787 criteria.isSingleRecord(),
788 con);
789 }
790
791 /***
792 * Utility method which executes a given sql statement. This
793 * method should be used for select statements only. Use
794 * executeStatement for update, insert, and delete operations.
795 *
796 * @param queryString A String with the sql statement to execute.
797 * @return List of Record objects.
798 * @throws TorqueException Any exceptions caught during processing will be
799 * rethrown wrapped into a TorqueException.
800 */
801 public static List executeQuery(String queryString) throws TorqueException
802 {
803 return executeQuery(queryString, Torque.getDefaultDB(), false);
804 }
805
806 /***
807 * Utility method which executes a given sql statement. This
808 * method should be used for select statements only. Use
809 * executeStatement for update, insert, and delete operations.
810 *
811 * @param queryString A String with the sql statement to execute.
812 * @param dbName The database to connect to.
813 * @return List of Record objects.
814 * @throws TorqueException Any exceptions caught during processing will be
815 * rethrown wrapped into a TorqueException.
816 */
817 public static List executeQuery(String queryString, String dbName)
818 throws TorqueException
819 {
820 return executeQuery(queryString, dbName, false);
821 }
822
823 /***
824 * Method for performing a SELECT. Returns all results.
825 *
826 * @param queryString A String with the sql statement to execute.
827 * @param dbName The database to connect to.
828 * @param singleRecord Whether or not we want to select only a
829 * single record.
830 * @return List of Record objects.
831 * @throws TorqueException Any exceptions caught during processing will be
832 * rethrown wrapped into a TorqueException.
833 */
834 public static List executeQuery(
835 String queryString,
836 String dbName,
837 boolean singleRecord)
838 throws TorqueException
839 {
840 return executeQuery(queryString, 0, -1, dbName, singleRecord);
841 }
842
843 /***
844 * Method for performing a SELECT. Returns all results.
845 *
846 * @param queryString A String with the sql statement to execute.
847 * @param singleRecord Whether or not we want to select only a
848 * single record.
849 * @param con A Connection.
850 * @return List of Record objects.
851 * @throws TorqueException Any exceptions caught during processing will be
852 * rethrown wrapped into a TorqueException.
853 */
854 public static List executeQuery(
855 String queryString,
856 boolean singleRecord,
857 Connection con)
858 throws TorqueException
859 {
860 return executeQuery(queryString, 0, -1, singleRecord, con);
861 }
862
863 /***
864 * Method for performing a SELECT.
865 *
866 * @param queryString A String with the sql statement to execute.
867 * @param start The first row to return.
868 * @param numberOfResults The number of rows to return.
869 * @param dbName The database to connect to.
870 * @param singleRecord Whether or not we want to select only a
871 * single record.
872 * @return List of Record objects.
873 * @throws TorqueException Any exceptions caught during processing will be
874 * rethrown wrapped into a TorqueException.
875 */
876 public static List executeQuery(
877 String queryString,
878 int start,
879 int numberOfResults,
880 String dbName,
881 boolean singleRecord)
882 throws TorqueException
883 {
884 Connection con = null;
885 List results = null;
886 try
887 {
888 con = Torque.getConnection(dbName);
889
890 results = executeQuery(
891 queryString,
892 start,
893 numberOfResults,
894 singleRecord,
895 con);
896 }
897 finally
898 {
899 Torque.closeConnection(con);
900 }
901 return results;
902 }
903
904 /***
905 * Method for performing a SELECT. Returns all results.
906 *
907 * @param queryString A String with the sql statement to execute.
908 * @param start The first row to return.
909 * @param numberOfResults The number of rows to return.
910 * @param singleRecord Whether or not we want to select only a
911 * single record.
912 * @param con A Connection.
913 * @return List of Record objects.
914 * @throws TorqueException Any exceptions caught during processing will be
915 * rethrown wrapped into a TorqueException.
916 */
917 public static List executeQuery(
918 String queryString,
919 int start,
920 int numberOfResults,
921 boolean singleRecord,
922 Connection con)
923 throws TorqueException
924 {
925 QueryDataSet qds = null;
926 List results = Collections.EMPTY_LIST;
927 try
928 {
929
930 long startTime = System.currentTimeMillis();
931 qds = new QueryDataSet(con, queryString);
932 if (log.isDebugEnabled())
933 {
934 log.debug("Elapsed time="
935 + (System.currentTimeMillis() - startTime) + " ms");
936 }
937 results = getSelectResults(
938 qds, start, numberOfResults, singleRecord);
939 }
940 catch (DataSetException e)
941 {
942 throwTorqueException(e);
943 }
944 catch (SQLException e)
945 {
946 throwTorqueException(e);
947 }
948 finally
949 {
950 VillageUtils.close(qds);
951 }
952 return results;
953 }
954
955 /***
956 * Returns all records in a QueryDataSet as a List of Record
957 * objects. Used for functionality like util.LargeSelect.
958 *
959 * @see #getSelectResults(QueryDataSet, int, int, boolean)
960 * @param qds the QueryDataSet
961 * @return a List of Record objects
962 * @throws TorqueException Any exceptions caught during processing will be
963 * rethrown wrapped into a TorqueException.
964 */
965 public static List getSelectResults(QueryDataSet qds)
966 throws TorqueException
967 {
968 return getSelectResults(qds, 0, -1, false);
969 }
970
971 /***
972 * Returns all records in a QueryDataSet as a List of Record
973 * objects. Used for functionality like util.LargeSelect.
974 *
975 * @see #getSelectResults(QueryDataSet, int, int, boolean)
976 * @param qds the QueryDataSet
977 * @param singleRecord
978 * @return a List of Record objects
979 * @throws TorqueException Any exceptions caught during processing will be
980 * rethrown wrapped into a TorqueException.
981 */
982 public static List getSelectResults(QueryDataSet qds, boolean singleRecord)
983 throws TorqueException
984 {
985 return getSelectResults(qds, 0, -1, singleRecord);
986 }
987
988 /***
989 * Returns numberOfResults records in a QueryDataSet as a List
990 * of Record objects. Starting at record 0. Used for
991 * functionality like util.LargeSelect.
992 *
993 * @see #getSelectResults(QueryDataSet, int, int, boolean)
994 * @param qds the QueryDataSet
995 * @param numberOfResults
996 * @param singleRecord
997 * @return a List of Record objects
998 * @throws TorqueException Any exceptions caught during processing will be
999 * rethrown wrapped into a TorqueException.
1000 */
1001 public static List getSelectResults(
1002 QueryDataSet qds,
1003 int numberOfResults,
1004 boolean singleRecord)
1005 throws TorqueException
1006 {
1007 List results = null;
1008 if (numberOfResults != 0)
1009 {
1010 results = getSelectResults(qds, 0, numberOfResults, singleRecord);
1011 }
1012 return results;
1013 }
1014
1015 /***
1016 * Returns numberOfResults records in a QueryDataSet as a List
1017 * of Record objects. Starting at record start. Used for
1018 * functionality like util.LargeSelect.
1019 *
1020 * @param qds The <code>QueryDataSet</code> to extract results
1021 * from.
1022 * @param start The index from which to start retrieving
1023 * <code>Record</code> objects from the data set.
1024 * @param numberOfResults The number of results to return (or
1025 * <code> -1</code> for all results).
1026 * @param singleRecord Whether or not we want to select only a
1027 * single record.
1028 * @return A <code>List</code> of <code>Record</code> objects.
1029 * @exception TorqueException If any <code>Exception</code> occurs.
1030 */
1031 public static List getSelectResults(
1032 QueryDataSet qds,
1033 int start,
1034 int numberOfResults,
1035 boolean singleRecord)
1036 throws TorqueException
1037 {
1038 List results = null;
1039 try
1040 {
1041 if (numberOfResults < 0)
1042 {
1043 results = new ArrayList();
1044 qds.fetchRecords();
1045 }
1046 else
1047 {
1048 results = new ArrayList(numberOfResults);
1049 qds.fetchRecords(start, numberOfResults);
1050 }
1051
1052 int startRecord = 0;
1053
1054
1055 if (start > 0 && numberOfResults <= 0)
1056 {
1057 startRecord = start;
1058 }
1059
1060
1061 for (int i = startRecord; i < qds.size(); i++)
1062 {
1063 Record rec = qds.getRecord(i);
1064 results.add(rec);
1065 }
1066
1067 if (results.size() > 1 && singleRecord)
1068 {
1069 handleMultipleRecords(qds);
1070 }
1071 }
1072 catch (Exception e)
1073 {
1074 throwTorqueException(e);
1075 }
1076 return results;
1077 }
1078
1079 /***
1080 * Helper method which returns the primary key contained
1081 * in the given Criteria object.
1082 *
1083 * @param criteria A Criteria.
1084 * @return ColumnMap if the Criteria object contains a primary
1085 * key, or null if it doesn't.
1086 * @throws TorqueException Any exceptions caught during processing will be
1087 * rethrown wrapped into a TorqueException.
1088 */
1089 private static ColumnMap getPrimaryKey(Criteria criteria)
1090 throws TorqueException
1091 {
1092
1093 String key = (String) criteria.keys().nextElement();
1094
1095 String table = criteria.getTableName(key);
1096 ColumnMap pk = null;
1097
1098 if (!table.equals(""))
1099 {
1100 DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName());
1101 if (dbMap == null)
1102 {
1103 throw new TorqueException("dbMap is null");
1104 }
1105 if (dbMap.getTable(table) == null)
1106 {
1107 throw new TorqueException("dbMap.getTable() is null");
1108 }
1109
1110 ColumnMap[] columns = dbMap.getTable(table).getColumns();
1111
1112 for (int i = 0; i < columns.length; i++)
1113 {
1114 if (columns[i].isPrimaryKey())
1115 {
1116 pk = columns[i];
1117 break;
1118 }
1119 }
1120 }
1121 return pk;
1122 }
1123
1124 /***
1125 * Convenience method used to update rows in the DB. Checks if a
1126 * <i>single</i> int primary key is specified in the Criteria
1127 * object and uses it to perform the udpate. If no primary key is
1128 * specified an Exception will be thrown.
1129 * <p>
1130 * Use this method for performing an update of the kind:
1131 * <p>
1132 * "WHERE primary_key_id = an int"
1133 * <p>
1134 * To perform an update with non-primary key fields in the WHERE
1135 * clause use doUpdate(criteria, criteria).
1136 *
1137 * @param updateValues A Criteria object containing values used in
1138 * set clause.
1139 * @throws TorqueException Any exceptions caught during processing will be
1140 * rethrown wrapped into a TorqueException.
1141 */
1142 public static void doUpdate(Criteria updateValues) throws TorqueException
1143 {
1144 Connection con = null;
1145 try
1146 {
1147 con = Transaction.beginOptional(
1148 updateValues.getDbName(),
1149 updateValues.isUseTransaction());
1150 doUpdate(updateValues, con);
1151 Transaction.commit(con);
1152 }
1153 catch (TorqueException e)
1154 {
1155 Transaction.safeRollback(con);
1156 throw e;
1157 }
1158 }
1159
1160 /***
1161 * Convenience method used to update rows in the DB. Checks if a
1162 * <i>single</i> int primary key is specified in the Criteria
1163 * object and uses it to perform the udpate. If no primary key is
1164 * specified an Exception will be thrown.
1165 * <p>
1166 * Use this method for performing an update of the kind:
1167 * <p>
1168 * "WHERE primary_key_id = an int"
1169 * <p>
1170 * To perform an update with non-primary key fields in the WHERE
1171 * clause use doUpdate(criteria, criteria).
1172 *
1173 * @param updateValues A Criteria object containing values used in
1174 * set clause.
1175 * @param con A Connection.
1176 * @throws TorqueException Any exceptions caught during processing will be
1177 * rethrown wrapped into a TorqueException.
1178 */
1179 public static void doUpdate(Criteria updateValues, Connection con)
1180 throws TorqueException
1181 {
1182 ColumnMap pk = getPrimaryKey(updateValues);
1183 Criteria selectCriteria = null;
1184
1185 if (pk != null && updateValues.containsKey(pk.getFullyQualifiedName()))
1186 {
1187 selectCriteria = new Criteria(2);
1188 selectCriteria.put(pk.getFullyQualifiedName(),
1189 updateValues.remove(pk.getFullyQualifiedName()));
1190 }
1191 else
1192 {
1193 throw new TorqueException("No PK specified for database update");
1194 }
1195
1196 doUpdate(selectCriteria, updateValues, con);
1197 }
1198
1199 /***
1200 * Method used to update rows in the DB. Rows are selected based
1201 * on selectCriteria and updated using values in updateValues.
1202 * <p>
1203 * Use this method for performing an update of the kind:
1204 * <p>
1205 * WHERE some_column = some value AND could_have_another_column =
1206 * another value AND so on...
1207 *
1208 * @param selectCriteria A Criteria object containing values used in where
1209 * clause.
1210 * @param updateValues A Criteria object containing values used in set
1211 * clause.
1212 * @throws TorqueException Any exceptions caught during processing will be
1213 * rethrown wrapped into a TorqueException.
1214 */
1215 public static void doUpdate(Criteria selectCriteria, Criteria updateValues)
1216 throws TorqueException
1217 {
1218 Connection con = null;
1219 try
1220 {
1221 con = Transaction.beginOptional(
1222 selectCriteria.getDbName(),
1223 updateValues.isUseTransaction());
1224 doUpdate(selectCriteria, updateValues, con);
1225 Transaction.commit(con);
1226 }
1227 catch (TorqueException e)
1228 {
1229 Transaction.safeRollback(con);
1230 throw e;
1231 }
1232 }
1233
1234 /***
1235 * Method used to update rows in the DB. Rows are selected based
1236 * on criteria and updated using values in updateValues.
1237 * <p>
1238 * Use this method for performing an update of the kind:
1239 * <p>
1240 * WHERE some_column = some value AND could_have_another_column =
1241 * another value AND so on.
1242 *
1243 * @param criteria A Criteria object containing values used in where
1244 * clause.
1245 * @param updateValues A Criteria object containing values used in set
1246 * clause.
1247 * @param con A Connection.
1248 * @throws TorqueException Any exceptions caught during processing will be
1249 * rethrown wrapped into a TorqueException.
1250 */
1251 public static void doUpdate(
1252 Criteria criteria,
1253 final Criteria updateValues,
1254 Connection con)
1255 throws TorqueException
1256 {
1257 Set tables = SQLBuilder.getTableSet(criteria, null);
1258
1259 try
1260 {
1261 processTables(criteria, tables, con, new ProcessCallback() {
1262 public void process (String table, String dbName, Record rec)
1263 throws Exception
1264 {
1265
1266 BasePeer.insertOrUpdateRecord(rec, table, dbName, updateValues);
1267 }
1268 });
1269 }
1270 catch (Exception e)
1271 {
1272 throwTorqueException(e);
1273 }
1274 }
1275
1276 /***
1277 * Utility method which executes a given sql statement. This
1278 * method should be used for update, insert, and delete
1279 * statements. Use executeQuery() for selects.
1280 *
1281 * @param statementString A String with the sql statement to execute.
1282 * @return The number of rows affected.
1283 * @throws TorqueException Any exceptions caught during processing will be
1284 * rethrown wrapped into a TorqueException.
1285 */
1286 public static int executeStatement(String statementString) throws TorqueException
1287 {
1288 return executeStatement(statementString, Torque.getDefaultDB());
1289 }
1290
1291 /***
1292 * Utility method which executes a given sql statement. This
1293 * method should be used for update, insert, and delete
1294 * statements. Use executeQuery() for selects.
1295 *
1296 * @param statementString A String with the sql statement to execute.
1297 * @param dbName Name of database to connect to.
1298 * @return The number of rows affected.
1299 * @throws TorqueException Any exceptions caught during processing will be
1300 * rethrown wrapped into a TorqueException.
1301 */
1302 public static int executeStatement(String statementString, String dbName)
1303 throws TorqueException
1304 {
1305 Connection con = null;
1306 int rowCount = -1;
1307 try
1308 {
1309 con = Torque.getConnection(dbName);
1310 rowCount = executeStatement(statementString, con);
1311 }
1312 finally
1313 {
1314 Torque.closeConnection(con);
1315 }
1316 return rowCount;
1317 }
1318
1319 /***
1320 * Utility method which executes a given sql statement. This
1321 * method should be used for update, insert, and delete
1322 * statements. Use executeQuery() for selects.
1323 *
1324 * @param statementString A String with the sql statement to execute.
1325 * @param con A Connection.
1326 * @return The number of rows affected.
1327 * @throws TorqueException Any exceptions caught during processing will be
1328 * rethrown wrapped into a TorqueException.
1329 */
1330 public static int executeStatement(String statementString, Connection con)
1331 throws TorqueException
1332 {
1333 int rowCount = -1;
1334 Statement statement = null;
1335 try
1336 {
1337 statement = con.createStatement();
1338 rowCount = statement.executeUpdate(statementString);
1339 }
1340 catch (SQLException e)
1341 {
1342 throw new TorqueException(e);
1343 }
1344 finally
1345 {
1346 if (statement != null)
1347 {
1348 try
1349 {
1350 statement.close();
1351 }
1352 catch (SQLException e)
1353 {
1354 throw new TorqueException(e);
1355 }
1356 }
1357 }
1358 return rowCount;
1359 }
1360
1361 /***
1362 * If the user specified that (s)he only wants to retrieve a
1363 * single record and multiple records are retrieved, this method
1364 * is called to handle the situation. The default behavior is to
1365 * throw an exception, but subclasses can override this method as
1366 * needed.
1367 *
1368 * @param ds The DataSet which contains multiple records.
1369 * @exception TooManyRowsException Couldn't handle multiple records.
1370 */
1371 protected static void handleMultipleRecords(DataSet ds)
1372 throws TooManyRowsException
1373 {
1374 throw new TooManyRowsException("Criteria expected single Record and "
1375 + "Multiple Records were selected");
1376 }
1377
1378 /***
1379 * This method returns the MapBuilder specified in the name
1380 * parameter. You should pass in the full path to the class, ie:
1381 * org.apache.torque.util.db.map.TurbineMapBuilder. The
1382 * MapBuilder instances are cached in the TorqueInstance for speed.
1383 *
1384 * @param name name of the MapBuilder
1385 * @return A MapBuilder, not null
1386 * @throws TorqueException if the Map Builder cannot be instantiated
1387 * @deprecated Use Torque.getMapBuilder(name) instead
1388 */
1389 public static MapBuilder getMapBuilder(String name)
1390 throws TorqueException
1391 {
1392 return Torque.getMapBuilder(name);
1393 }
1394
1395 /***
1396 * Performs a SQL <code>select</code> using a PreparedStatement.
1397 * Note: this method does not handle null criteria values.
1398 *
1399 * @param criteria
1400 * @param con
1401 * @return a List of Record objects.
1402 * @throws TorqueException Error performing database query.
1403 */
1404 public static List doPSSelect(Criteria criteria, Connection con)
1405 throws TorqueException
1406 {
1407 List v = null;
1408
1409 StringBuffer qry = new StringBuffer();
1410 List params = new ArrayList(criteria.size());
1411
1412 createPreparedStatement(criteria, qry, params);
1413
1414 PreparedStatement statement = null;
1415 try
1416 {
1417 statement = con.prepareStatement(qry.toString());
1418
1419 for (int i = 0; i < params.size(); i++)
1420 {
1421 Object param = params.get(i);
1422 if (param instanceof java.sql.Date)
1423 {
1424 statement.setDate(i + 1, (java.sql.Date) param);
1425 }
1426 else if (param instanceof NumberKey)
1427 {
1428 statement.setBigDecimal(i + 1,
1429 ((NumberKey) param).getBigDecimal());
1430 }
1431 else if (param instanceof Integer)
1432 {
1433 statement.setInt(i + 1, ((Integer) param).intValue());
1434 }
1435 else
1436 {
1437 statement.setString(i + 1, param.toString());
1438 }
1439 }
1440
1441 QueryDataSet qds = null;
1442 try
1443 {
1444 qds = new QueryDataSet(statement.executeQuery());
1445 v = getSelectResults(qds);
1446 }
1447 finally
1448 {
1449 VillageUtils.close(qds);
1450 }
1451 }
1452 catch (DataSetException e)
1453 {
1454 throwTorqueException(e);
1455 }
1456 catch (SQLException e)
1457 {
1458 throwTorqueException(e);
1459 }
1460 finally
1461 {
1462 if (statement != null)
1463 {
1464 try
1465 {
1466 statement.close();
1467 }
1468 catch (SQLException e)
1469 {
1470 throw new TorqueException(e);
1471 }
1472 }
1473 }
1474 return v;
1475 }
1476
1477 /***
1478 * Do a Prepared Statement select according to the given criteria
1479 *
1480 * @param criteria
1481 * @return a List of Record objects.
1482 * @throws TorqueException Any exceptions caught during processing will be
1483 * rethrown wrapped into a TorqueException.
1484 */
1485 public static List doPSSelect(Criteria criteria) throws TorqueException
1486 {
1487 Connection con = Torque.getConnection(criteria.getDbName());
1488 List v = null;
1489
1490 try
1491 {
1492 v = doPSSelect(criteria, con);
1493 }
1494 finally
1495 {
1496 Torque.closeConnection(con);
1497 }
1498 return v;
1499 }
1500
1501 /***
1502 * Create a new PreparedStatement. It builds a string representation
1503 * of a query and a list of PreparedStatement parameters.
1504 *
1505 * @param criteria
1506 * @param queryString
1507 * @param params
1508 * @throws TorqueException Any exceptions caught during processing will be
1509 * rethrown wrapped into a TorqueException.
1510 */
1511 public static void createPreparedStatement(
1512 Criteria criteria,
1513 StringBuffer queryString,
1514 List params)
1515 throws TorqueException
1516 {
1517 Query query = SQLBuilder.buildQueryClause(criteria, params, new SQLBuilder.QueryCallback() {
1518 public String process(Criteria.Criterion criterion, List params)
1519 {
1520 StringBuffer sb = new StringBuffer();
1521 criterion.appendPsTo(sb, params);
1522 return sb.toString();
1523 }
1524 });
1525
1526 String sql = query.toString();
1527 log.debug(sql);
1528
1529 queryString.append(sql);
1530 }
1531
1532 /***
1533 * Checks all columns in the criteria to see whether
1534 * booleanchar and booleanint columns are queried with a boolean.
1535 * If yes, the query values are mapped onto values the database
1536 * does understand, i.e. 0 and 1 for booleanints and N and Y for
1537 * booleanchar columns.
1538 *
1539 * @param criteria The criteria to be checked for booleanint and booleanchar
1540 * columns.
1541 * @param defaultTableMap the table map to be used if the table name is
1542 * not given in a column.
1543 * @throws TorqueException if the database map for the criteria cannot be
1544 * retrieved.
1545 */
1546 public static void correctBooleans(
1547 Criteria criteria,
1548 TableMap defaultTableMap)
1549 throws TorqueException
1550 {
1551 Iterator keyIt = criteria.keySet().iterator();
1552 while (keyIt.hasNext())
1553 {
1554 String key = (String) keyIt.next();
1555 String columnName;
1556 TableMap tableMap = null;
1557 int dotPosition = key.lastIndexOf(".");
1558 if (dotPosition == -1)
1559 {
1560 columnName = key;
1561 tableMap = defaultTableMap;
1562 }
1563 else
1564 {
1565 columnName = key.substring(dotPosition + 1);
1566 String tableName = key.substring(0, dotPosition);
1567 String databaseName = criteria.getDbName();
1568 if (databaseName == null)
1569 {
1570 databaseName = Torque.getDefaultDB();
1571 }
1572 DatabaseMap databaseMap = Torque.getDatabaseMap(databaseName);
1573 if (databaseMap != null)
1574 {
1575 tableMap = databaseMap.getTable(tableName);
1576 }
1577 if (tableMap == null)
1578 {
1579
1580 Map aliases = criteria.getAliases();
1581 if (aliases.get(tableName) != null)
1582 {
1583 tableName = (String) aliases.get(tableName);
1584 tableMap = databaseMap.getTable(tableName);
1585 }
1586 }
1587 }
1588 if (tableMap == null)
1589 {
1590
1591 continue;
1592 }
1593
1594 ColumnMap columnMap = tableMap.getColumn(columnName);
1595 if (columnMap != null)
1596 {
1597 if ("BOOLEANINT".equals(columnMap.getTorqueType()))
1598 {
1599 Criteria.Criterion criterion = criteria.getCriterion(key);
1600 replaceBooleanValues(
1601 criterion,
1602 new Integer(1),
1603 new Integer(0));
1604 }
1605 else if ("BOOLEANCHAR".equals(columnMap.getTorqueType()))
1606 {
1607 Criteria.Criterion criterion = criteria.getCriterion(key);
1608 replaceBooleanValues(criterion, "Y", "N");
1609 }
1610 }
1611 }
1612 }
1613
1614 /***
1615 * Replaces any Boolean value in the criterion and its attached Criterions
1616 * by trueValue if the Boolean equals <code>Boolean.TRUE</code>
1617 * and falseValue if the Boolean equals <code>Boolean.FALSE</code>.
1618 *
1619 * @param criterion the criterion to replace Boolean values in.
1620 * @param trueValue the value by which Boolean.TRUE should be replaced.
1621 * @param falseValue the value by which Boolean.FALSE should be replaced.
1622 */
1623 private static void replaceBooleanValues(
1624 Criteria.Criterion criterion,
1625 Object trueValue,
1626 Object falseValue)
1627 {
1628
1629
1630 Criteria.Criterion[] attachedCriterions
1631 = criterion.getAttachedCriterion();
1632 for (int i = 0; i < attachedCriterions.length; ++i)
1633 {
1634 Object criterionValue
1635 = attachedCriterions[i].getValue();
1636 if (criterionValue instanceof Boolean)
1637 {
1638 Boolean booleanValue = (Boolean) criterionValue;
1639 attachedCriterions[i].setValue(
1640 Boolean.TRUE.equals(booleanValue)
1641 ? trueValue
1642 : falseValue);
1643 }
1644
1645 }
1646
1647 }
1648
1649 /***
1650 * Process the result of a Table list generation.
1651 * This runs the statements onto the list of tables and
1652 * provides a callback hook to add functionality.
1653 *
1654 * This method should've been in SQLBuilder, but is uses the handleMultipleRecords callback thingie..
1655 *
1656 * @param crit The criteria
1657 * @param tables A set of Tables to run on
1658 * @param con The SQL Connection to run the statements on
1659 * @param pc A ProcessCallback object
1660 *
1661 * @throws Exception An Error occured (should be wrapped into TorqueException)
1662 */
1663 private static void processTables(Criteria crit, Set tables, Connection con, ProcessCallback pc)
1664 throws Exception
1665 {
1666 String dbName = crit.getDbName();
1667 DB db = Torque.getDB(dbName);
1668 DatabaseMap dbMap = Torque.getDatabaseMap(dbName);
1669
1670
1671 for (Iterator it = tables.iterator(); it.hasNext();)
1672 {
1673 String table = (String) it.next();
1674 KeyDef kd = new KeyDef();
1675 Set whereClause = new HashSet();
1676
1677 ColumnMap[] columnMaps = dbMap.getTable(table).getColumns();
1678
1679 for (int j = 0; j < columnMaps.length; j++)
1680 {
1681 ColumnMap colMap = columnMaps[j];
1682 if (colMap.isPrimaryKey())
1683 {
1684 kd.addAttrib(colMap.getColumnName());
1685 }
1686
1687 String key = new StringBuffer(colMap.getTableName())
1688 .append('.')
1689 .append(colMap.getColumnName())
1690 .toString();
1691
1692 if (crit.containsKey(key))
1693 {
1694 if (crit
1695 .getComparison(key)
1696 .equals(Criteria.CUSTOM))
1697 {
1698 whereClause.add(crit.getString(key));
1699 }
1700 else
1701 {
1702 whereClause.add(
1703 SqlExpression.build(
1704 colMap.getColumnName(),
1705 crit.getValue(key),
1706 crit.getComparison(key),
1707 crit.isIgnoreCase(),
1708 db));
1709 }
1710 }
1711 }
1712
1713
1714 TableDataSet tds = null;
1715 try
1716 {
1717 String tableName = SQLBuilder.getFullTableName(table, dbName);
1718
1719
1720 tds = new TableDataSet(con, tableName, kd);
1721 String sqlSnippet = StringUtils.join(whereClause.iterator(), " AND ");
1722
1723 if (log.isDebugEnabled())
1724 {
1725 log.debug("BasePeer: whereClause=" + sqlSnippet);
1726 }
1727
1728 tds.where(sqlSnippet);
1729 tds.fetchRecords();
1730
1731 if (tds.size() > 1 && crit.isSingleRecord())
1732 {
1733 handleMultipleRecords(tds);
1734 }
1735
1736 for (int j = 0; j < tds.size(); j++)
1737 {
1738 Record rec = tds.getRecord(j);
1739
1740 if (pc != null)
1741 {
1742
1743 pc.process(table, dbName, rec);
1744 }
1745 }
1746 }
1747 finally
1748 {
1749 VillageUtils.close(tds);
1750 }
1751 }
1752 }
1753
1754 /***
1755 * Inner Interface that defines the Callback method for
1756 * the Record Processing
1757 */
1758 protected interface ProcessCallback
1759 {
1760 void process (String table, String dbName, Record rec)
1761 throws Exception;
1762 }
1763 }