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