0001: package org.apache.torque.util;
0002:
0003: /*
0004: * Licensed to the Apache Software Foundation (ASF) under one
0005: * or more contributor license agreements. See the NOTICE file
0006: * distributed with this work for additional information
0007: * regarding copyright ownership. The ASF licenses this file
0008: * to you under the Apache License, Version 2.0 (the
0009: * "License"); you may not use this file except in compliance
0010: * with the License. You may obtain a copy of the License at
0011: *
0012: * http://www.apache.org/licenses/LICENSE-2.0
0013: *
0014: * Unless required by applicable law or agreed to in writing,
0015: * software distributed under the License is distributed on an
0016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0017: * KIND, either express or implied. See the License for the
0018: * specific language governing permissions and limitations
0019: * under the License.
0020: */
0021:
0022: import java.io.Serializable;
0023: import java.sql.Connection;
0024: import java.sql.PreparedStatement;
0025: import java.sql.SQLException;
0026: import java.sql.Statement;
0027: import java.util.ArrayList;
0028: import java.util.Collections;
0029: import java.util.HashSet;
0030: import java.util.Iterator;
0031: import java.util.List;
0032: import java.util.Map;
0033: import java.util.Set;
0034:
0035: import org.apache.commons.lang.StringUtils;
0036: import org.apache.commons.logging.Log;
0037: import org.apache.commons.logging.LogFactory;
0038: import org.apache.torque.Database;
0039: import org.apache.torque.Torque;
0040: import org.apache.torque.TorqueException;
0041: import org.apache.torque.adapter.DB;
0042: import org.apache.torque.map.ColumnMap;
0043: import org.apache.torque.map.DatabaseMap;
0044: import org.apache.torque.map.MapBuilder;
0045: import org.apache.torque.map.TableMap;
0046: import org.apache.torque.oid.IdGenerator;
0047: import org.apache.torque.om.NumberKey;
0048: import org.apache.torque.om.ObjectKey;
0049: import org.apache.torque.om.SimpleKey;
0050: import org.apache.torque.om.StringKey;
0051:
0052: import com.workingdogs.village.Column;
0053: import com.workingdogs.village.DataSet;
0054: import com.workingdogs.village.DataSetException;
0055: import com.workingdogs.village.KeyDef;
0056: import com.workingdogs.village.QueryDataSet;
0057: import com.workingdogs.village.Record;
0058: import com.workingdogs.village.Schema;
0059: import com.workingdogs.village.TableDataSet;
0060:
0061: /**
0062: * This is the base class for all Peer classes in the system. Peer
0063: * classes are responsible for isolating all of the database access
0064: * for a specific business object. They execute all of the SQL
0065: * against the database. Over time this class has grown to include
0066: * utility methods which ease execution of cross-database queries and
0067: * the implementation of concrete Peers.
0068: *
0069: * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
0070: * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
0071: * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
0072: * @author <a href="mailto:stephenh@chase3000.com">Stephen Haberman</a>
0073: * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
0074: * @author <a href="mailto:vido@ldh.org">Augustin Vidovic</a>
0075: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
0076: * @version $Id: BasePeer.java 591648 2007-11-03 16:34:00Z tfischer $
0077: */
0078: public abstract class BasePeer implements Serializable {
0079: /** Constant criteria key to reference ORDER BY columns. */
0080: public static final String ORDER_BY = "ORDER BY";
0081:
0082: /**
0083: * Constant criteria key to remove Case Information from
0084: * search/ordering criteria.
0085: */
0086: public static final String IGNORE_CASE = "IgNOrE cAsE";
0087:
0088: /** Classes that implement this class should override this value. */
0089: public static final String TABLE_NAME = "TABLE_NAME";
0090:
0091: /** the log */
0092: protected static final Log log = LogFactory.getLog(BasePeer.class);
0093:
0094: private static void throwTorqueException(Exception e)
0095: throws TorqueException {
0096: if (e instanceof TorqueException) {
0097: throw (TorqueException) e;
0098: } else {
0099: throw new TorqueException(e);
0100: }
0101: }
0102:
0103: /**
0104: * Sets up a Schema for a table. This schema is then normally
0105: * used as the argument for initTableColumns().
0106: *
0107: * @param tableName The name of the table.
0108: * @return A Schema.
0109: */
0110: public static Schema initTableSchema(String tableName) {
0111: return initTableSchema(tableName, Torque.getDefaultDB());
0112: }
0113:
0114: /**
0115: * Sets up a Schema for a table. This schema is then normally
0116: * used as the argument for initTableColumns
0117: *
0118: * @param tableName The propery name for the database in the
0119: * configuration file.
0120: * @param dbName The name of the database.
0121: * @return A Schema.
0122: */
0123: public static Schema initTableSchema(String tableName, String dbName) {
0124: Schema schema = null;
0125: Connection con = null;
0126:
0127: try {
0128: con = Torque.getConnection(dbName);
0129: schema = new Schema().schema(con, tableName);
0130: } catch (Exception e) {
0131: log.error(e);
0132: throw new Error("Error in BasePeer.initTableSchema("
0133: + tableName + "): " + e.getMessage());
0134: } finally {
0135: Torque.closeConnection(con);
0136: }
0137: return schema;
0138: }
0139:
0140: /**
0141: * Creates a Column array for a table based on its Schema.
0142: *
0143: * @param schema A Schema object.
0144: * @return A Column[].
0145: */
0146: public static Column[] initTableColumns(Schema schema) {
0147: Column[] columns = null;
0148: try {
0149: int numberOfColumns = schema.numberOfColumns();
0150: columns = new Column[numberOfColumns];
0151: for (int i = 0; i < numberOfColumns; i++) {
0152: columns[i] = schema.column(i + 1);
0153: }
0154: } catch (Exception e) {
0155: log.error(e);
0156: throw new Error("Error in BasePeer.initTableColumns(): "
0157: + e.getMessage());
0158: }
0159: return columns;
0160: }
0161:
0162: /**
0163: * Convenience method to create a String array of column names.
0164: *
0165: * @param columns A Column[].
0166: * @return A String[].
0167: */
0168: public static String[] initColumnNames(Column[] columns) {
0169: String[] columnNames = new String[columns.length];
0170: for (int i = 0; i < columns.length; i++) {
0171: columnNames[i] = columns[i].name().toUpperCase();
0172: }
0173: return columnNames;
0174: }
0175:
0176: /**
0177: * Convenience method to create a String array of criteria keys.
0178: *
0179: * @param tableName Name of table.
0180: * @param columnNames A String[].
0181: * @return A String[].
0182: */
0183: public static String[] initCriteriaKeys(String tableName,
0184: String[] columnNames) {
0185: String[] keys = new String[columnNames.length];
0186: for (int i = 0; i < columnNames.length; i++) {
0187: keys[i] = tableName + "." + columnNames[i].toUpperCase();
0188: }
0189: return keys;
0190: }
0191:
0192: /**
0193: * Convenience method that uses straight JDBC to delete multiple
0194: * rows. Village throws an Exception when multiple rows are
0195: * deleted.
0196: *
0197: * @param con A Connection.
0198: * @param table The table to delete records from.
0199: * @param column The column in the where clause.
0200: * @param value The value of the column.
0201: * @throws TorqueException Any exceptions caught during processing will be
0202: * rethrown wrapped into a TorqueException.
0203: */
0204: public static void deleteAll(Connection con, String table,
0205: String column, int value) throws TorqueException {
0206: Statement statement = null;
0207: try {
0208: statement = con.createStatement();
0209:
0210: StringBuffer query = new StringBuffer();
0211: query.append("DELETE FROM ").append(table)
0212: .append(" WHERE ").append(column).append(" = ")
0213: .append(value);
0214:
0215: statement.executeUpdate(query.toString());
0216: } catch (SQLException e) {
0217: throw new TorqueException(e);
0218: } finally {
0219: if (statement != null) {
0220: try {
0221: statement.close();
0222: } catch (SQLException e) {
0223: throw new TorqueException(e);
0224: }
0225: }
0226: }
0227: }
0228:
0229: /**
0230: * Convenience method that uses straight JDBC to delete multiple
0231: * rows. Village throws an Exception when multiple rows are
0232: * deleted. This method attempts to get the default database from
0233: * the pool.
0234: *
0235: * @param table The table to delete records from.
0236: * @param column The column in the where clause.
0237: * @param value The value of the column.
0238: * @throws TorqueException Any exceptions caught during processing will be
0239: * rethrown wrapped into a TorqueException.
0240: */
0241: public static void deleteAll(String table, String column, int value)
0242: throws TorqueException {
0243: Connection con = null;
0244: try {
0245: // Get a connection to the db.
0246: con = Torque.getConnection(Torque.getDefaultDB());
0247: deleteAll(con, table, column, value);
0248: } finally {
0249: Torque.closeConnection(con);
0250: }
0251: }
0252:
0253: /**
0254: * Method to perform deletes based on values and keys in a
0255: * Criteria.
0256: *
0257: * @param criteria The criteria to use.
0258: * @throws TorqueException Any exceptions caught during processing will be
0259: * rethrown wrapped into a TorqueException.
0260: * @deprecated This method causes unexpected results when joins are used.
0261: * Please use doDelete(Criteria, String).
0262: */
0263: public static void doDelete(Criteria criteria)
0264: throws TorqueException {
0265: doDelete(criteria, (String) null);
0266: }
0267:
0268: /**
0269: * Method to perform deletes based on values and keys in a
0270: * Criteria.
0271: * This method is protected because it may cause ambiguity between
0272: * doDelete(Criteria,Connection) and this method. It will be made public
0273: * once doDelete(Criteria, Connection) is removed.
0274: *
0275: * @param criteria The criteria to use.
0276: * @param tableName the name of the table to delete records from.
0277: * If set to null, the name of the table(s) can be extracted from
0278: * the criteria, but this can cause unexpected results.
0279: * @throws TorqueException Any exceptions caught during processing will be
0280: * rethrown wrapped into a TorqueException.
0281: */
0282: protected static void doDelete(Criteria criteria, String tableName)
0283: throws TorqueException {
0284: Connection con = null;
0285: try {
0286: con = Transaction.beginOptional(criteria.getDbName(),
0287: criteria.isUseTransaction());
0288: doDelete(criteria, tableName, con);
0289: Transaction.commit(con);
0290: } catch (TorqueException e) {
0291: Transaction.safeRollback(con);
0292: throw e;
0293: }
0294: }
0295:
0296: /**
0297: * Method to perform deletes based on values and keys in a Criteria.
0298: *
0299: * @param criteria The criteria to use.
0300: * @param con A Connection.
0301: * @throws TorqueException Any exceptions caught during processing will be
0302: * rethrown wrapped into a TorqueException.
0303: * @deprecated This method causes unexpected results when joins are used.
0304: * Please use doDelete(Criteria, String, Connection).
0305: */
0306: public static void doDelete(Criteria criteria, Connection con)
0307: throws TorqueException {
0308: doDelete(criteria, null, con);
0309: }
0310:
0311: /**
0312: * Method to perform deletes based on values and keys in a Criteria.
0313: *
0314: * @param criteria The criteria to use.
0315: * @param tableName the name of the table to delete records from.
0316: * If set to null, the name of the table(s) can be extracted from
0317: * the criteria, but this can cause unexpected results.
0318: * @param con A Connection.
0319: * @throws TorqueException Any exceptions caught during processing will be
0320: * rethrown wrapped into a TorqueException.
0321: */
0322: public static void doDelete(Criteria criteria, String tableName,
0323: Connection con) throws TorqueException {
0324: String dbName = criteria.getDbName();
0325: final DatabaseMap dbMap = Torque.getDatabaseMap(dbName);
0326:
0327: // This Callback adds all tables to the Table set which
0328: // are referenced from a cascading criteria. As a result, all
0329: // data that is referenced through foreign keys will also be
0330: // deleted.
0331: SQLBuilder.TableCallback tc = new SQLBuilder.TableCallback() {
0332: public void process(Set tables, String key, Criteria crit) {
0333: if (crit.isCascade()) {
0334: // This steps thru all the columns in the database.
0335: TableMap[] tableMaps = dbMap.getTables();
0336: for (int i = 0; i < tableMaps.length; i++) {
0337: ColumnMap[] columnMaps = tableMaps[i]
0338: .getColumns();
0339:
0340: for (int j = 0; j < columnMaps.length; j++) {
0341: // Only delete rows where the foreign key is
0342: // also a primary key. Other rows need
0343: // updating, but that is not implemented.
0344: if (columnMaps[j].isForeignKey()
0345: && columnMaps[j].isPrimaryKey()
0346: && key.equals(columnMaps[j]
0347: .getRelatedName())) {
0348: tables.add(tableMaps[i].getName());
0349: crit.add(columnMaps[j]
0350: .getFullyQualifiedName(), crit
0351: .getValue(key));
0352: }
0353: }
0354: }
0355: }
0356: }
0357: };
0358:
0359: Set tables;
0360: if (tableName == null) {
0361: tables = SQLBuilder.getTableSet(criteria, tc);
0362: } else {
0363: tables = new HashSet(1);
0364: tables.add(tableName);
0365: }
0366:
0367: try {
0368: processTables(criteria, tables, con, new ProcessCallback() {
0369: public void process(String table, String dbName,
0370: Record rec) throws Exception {
0371: rec.markToBeDeleted();
0372: rec.save();
0373: }
0374: });
0375: } catch (Exception e) {
0376: throwTorqueException(e);
0377: }
0378: }
0379:
0380: /**
0381: * Method to perform inserts based on values and keys in a
0382: * Criteria.
0383: * <p>
0384: * If the primary key is auto incremented the data in Criteria
0385: * will be inserted and the auto increment value will be returned.
0386: * <p>
0387: * If the primary key is included in Criteria then that value will
0388: * be used to insert the row.
0389: * <p>
0390: * If no primary key is included in Criteria then we will try to
0391: * figure out the primary key from the database map and insert the
0392: * row with the next available id using util.db.IDBroker.
0393: * <p>
0394: * If no primary key is defined for the table the values will be
0395: * inserted as specified in Criteria and -1 will be returned.
0396: *
0397: * @param criteria Object containing values to insert.
0398: * @return An Object which is the id of the row that was inserted
0399: * (if the table has a primary key) or null (if the table does not
0400: * have a primary key).
0401: * @throws TorqueException Any exceptions caught during processing will be
0402: * rethrown wrapped into a TorqueException.
0403: */
0404: public static ObjectKey doInsert(Criteria criteria)
0405: throws TorqueException {
0406: Connection con = null;
0407: ObjectKey id = null;
0408:
0409: try {
0410: con = Transaction.beginOptional(criteria.getDbName(),
0411: criteria.isUseTransaction());
0412: id = doInsert(criteria, con);
0413: Transaction.commit(con);
0414: } catch (TorqueException e) {
0415: Transaction.safeRollback(con);
0416: throw e;
0417: }
0418:
0419: return id;
0420: }
0421:
0422: /**
0423: * Method to perform inserts based on values and keys in a
0424: * Criteria.
0425: * <p>
0426: * If the primary key is auto incremented the data in Criteria
0427: * will be inserted and the auto increment value will be returned.
0428: * <p>
0429: * If the primary key is included in Criteria then that value will
0430: * be used to insert the row.
0431: * <p>
0432: * If no primary key is included in Criteria then we will try to
0433: * figure out the primary key from the database map and insert the
0434: * row with the next available id using util.db.IDBroker.
0435: * <p>
0436: * If no primary key is defined for the table the values will be
0437: * inserted as specified in Criteria and null will be returned.
0438: *
0439: * @param criteria Object containing values to insert.
0440: * @param con A Connection.
0441: * @return An Object which is the id of the row that was inserted
0442: * (if the table has a primary key) or null (if the table does not
0443: * have a primary key).
0444: * @throws TorqueException Any exceptions caught during processing will be
0445: * rethrown wrapped into a TorqueException.
0446: */
0447: public static ObjectKey doInsert(Criteria criteria, Connection con)
0448: throws TorqueException {
0449: SimpleKey id = null;
0450:
0451: // Get the table name and method for determining the primary
0452: // key value.
0453: String table = null;
0454: Iterator keys = criteria.keySet().iterator();
0455: if (keys.hasNext()) {
0456: table = criteria.getTableName((String) keys.next());
0457: } else {
0458: throw new TorqueException(
0459: "Database insert attempted without "
0460: + "anything specified to insert");
0461: }
0462:
0463: String dbName = criteria.getDbName();
0464: Database database = Torque.getDatabase(dbName);
0465: DatabaseMap dbMap = database.getDatabaseMap();
0466: TableMap tableMap = dbMap.getTable(table);
0467: Object keyInfo = tableMap.getPrimaryKeyMethodInfo();
0468: IdGenerator keyGen = database.getIdGenerator(tableMap
0469: .getPrimaryKeyMethod());
0470:
0471: ColumnMap pk = getPrimaryKey(criteria);
0472:
0473: // If the keyMethod is SEQUENCE or IDBROKERTABLE, get the id
0474: // before the insert.
0475: if (keyGen != null && keyGen.isPriorToInsert()) {
0476: // pk will be null if there is no primary key defined for the table
0477: // we're inserting into.
0478: if (pk != null
0479: && !criteria
0480: .containsKey(pk.getFullyQualifiedName())) {
0481: id = getId(pk, keyGen, con, keyInfo);
0482: criteria.add(pk.getFullyQualifiedName(), id);
0483: }
0484: }
0485:
0486: // Use Village to perform the insert.
0487: TableDataSet tds = null;
0488: try {
0489: String tableName = SQLBuilder.getFullTableName(table,
0490: dbName);
0491: tds = new TableDataSet(con, tableName);
0492: Record rec = tds.addRecord();
0493: // not the fully qualified name, insertOrUpdateRecord wants to use table as an index...
0494: BasePeer.insertOrUpdateRecord(rec, table, dbName, criteria);
0495: } catch (DataSetException e) {
0496: throwTorqueException(e);
0497: } catch (SQLException e) {
0498: throwTorqueException(e);
0499: } catch (TorqueException e) {
0500: throwTorqueException(e);
0501: } finally {
0502: VillageUtils.close(tds);
0503: }
0504:
0505: // If the primary key column is auto-incremented, get the id
0506: // now.
0507: if (keyGen != null && keyGen.isPostInsert()) {
0508: id = getId(pk, keyGen, con, keyInfo);
0509: }
0510:
0511: return id;
0512: }
0513:
0514: /**
0515: * Create an Id for insertion in the Criteria
0516: *
0517: * @param pk ColumnMap for the Primary key
0518: * @param keyGen The Id Generator object
0519: * @param con The SQL Connection to run the id generation under
0520: * @param keyInfo KeyInfo Parameter from the Table map
0521: *
0522: * @return A simple Key representing the new Id value
0523: * @throws TorqueException Possible errors get wrapped in here.
0524: */
0525: private static SimpleKey getId(ColumnMap pk, IdGenerator keyGen,
0526: Connection con, Object keyInfo) throws TorqueException {
0527: SimpleKey id = null;
0528:
0529: try {
0530: if (pk != null && keyGen != null) {
0531: if (pk.getType() instanceof Number) {
0532: id = new NumberKey(keyGen.getIdAsBigDecimal(con,
0533: keyInfo));
0534: } else {
0535: id = new StringKey(keyGen.getIdAsString(con,
0536: keyInfo));
0537: }
0538: }
0539: } catch (Exception e) {
0540: throwTorqueException(e);
0541: }
0542: return id;
0543: }
0544:
0545: /**
0546: * Grouping of code used in both doInsert() and doUpdate()
0547: * methods. Sets up a Record for saving.
0548: *
0549: * @param rec A Record.
0550: * @param table Name of table.
0551: * @param criteria A Criteria.
0552: * @throws TorqueException Any exceptions caught during processing will be
0553: * rethrown wrapped into a TorqueException.
0554: */
0555: private static void insertOrUpdateRecord(Record rec, String table,
0556: String dbName, Criteria criteria) throws TorqueException {
0557: DatabaseMap dbMap = Torque.getDatabaseMap(dbName);
0558:
0559: ColumnMap[] columnMaps = dbMap.getTable(table).getColumns();
0560: boolean shouldSave = false;
0561: for (int j = 0; j < columnMaps.length; j++) {
0562: ColumnMap colMap = columnMaps[j];
0563: String colName = colMap.getColumnName();
0564: String key = new StringBuffer(colMap.getTableName())
0565: .append('.').append(colName).toString();
0566: if (criteria.containsKey(key)) {
0567: try {
0568: VillageUtils.setVillageValue(criteria, key, rec,
0569: colName);
0570: shouldSave = true;
0571: } catch (Exception e) {
0572: throwTorqueException(e);
0573: }
0574: }
0575: }
0576:
0577: if (shouldSave) {
0578: try {
0579: rec.save();
0580: } catch (Exception e) {
0581: throwTorqueException(e);
0582: }
0583: } else {
0584: throw new TorqueException("No changes to save");
0585: }
0586: }
0587:
0588: /**
0589: * Method to create an SQL query for display only based on values in a
0590: * Criteria.
0591: *
0592: * @param criteria A Criteria.
0593: * @return the SQL query for display
0594: * @exception TorqueException Trouble creating the query string.
0595: */
0596: static String createQueryDisplayString(Criteria criteria)
0597: throws TorqueException {
0598: return createQuery(criteria).toString();
0599: }
0600:
0601: /**
0602: * Method to create an SQL query for actual execution based on values in a
0603: * Criteria.
0604: *
0605: * @param criteria A Criteria.
0606: * @return the SQL query for actual execution
0607: * @exception TorqueException Trouble creating the query string.
0608: */
0609: public static String createQueryString(Criteria criteria)
0610: throws TorqueException {
0611: Query query = createQuery(criteria);
0612: return query.toString();
0613: }
0614:
0615: /**
0616: * Method to create an SQL query based on values in a Criteria. Note that
0617: * final manipulation of the limit and offset are performed when the query
0618: * is actually executed.
0619: *
0620: * @param criteria A Criteria.
0621: * @return the sql query
0622: * @exception TorqueException Trouble creating the query string.
0623: */
0624: static Query createQuery(Criteria criteria) throws TorqueException {
0625: return SQLBuilder.buildQueryClause(criteria, null,
0626: new SQLBuilder.QueryCallback() {
0627: public String process(Criteria.Criterion criterion,
0628: List params) {
0629: return criterion.toString();
0630: }
0631: });
0632: }
0633:
0634: /**
0635: * Returns all results.
0636: *
0637: * @param criteria A Criteria.
0638: * @return List of Record objects.
0639: * @throws TorqueException Any exceptions caught during processing will be
0640: * rethrown wrapped into a TorqueException.
0641: */
0642: public static List doSelect(Criteria criteria)
0643: throws TorqueException {
0644: Connection con = null;
0645: List results = null;
0646:
0647: try {
0648: con = Transaction.beginOptional(criteria.getDbName(),
0649: criteria.isUseTransaction());
0650: results = doSelect(criteria, con);
0651: Transaction.commit(con);
0652: } catch (TorqueException e) {
0653: Transaction.safeRollback(con);
0654: throw e;
0655: }
0656: return results;
0657: }
0658:
0659: /**
0660: * Returns all results.
0661: *
0662: * @param criteria A Criteria.
0663: * @param con A Connection.
0664: * @return List of Record objects.
0665: * @throws TorqueException Any exceptions caught during processing will be
0666: * rethrown wrapped into a TorqueException.
0667: */
0668: public static List doSelect(Criteria criteria, Connection con)
0669: throws TorqueException {
0670: Query query = createQuery(criteria);
0671: DB dbadapter = Torque.getDB(criteria.getDbName());
0672:
0673: // Call Village depending on the capabilities of the DB
0674: return executeQuery(query.toString(), dbadapter
0675: .supportsNativeOffset() ? 0 : criteria.getOffset(),
0676: dbadapter.supportsNativeLimit() ? -1 : criteria
0677: .getLimit(), criteria.isSingleRecord(), con);
0678: }
0679:
0680: /**
0681: * Utility method which executes a given sql statement. This
0682: * method should be used for select statements only. Use
0683: * executeStatement for update, insert, and delete operations.
0684: *
0685: * @param queryString A String with the sql statement to execute.
0686: * @return List of Record objects.
0687: * @throws TorqueException Any exceptions caught during processing will be
0688: * rethrown wrapped into a TorqueException.
0689: */
0690: public static List executeQuery(String queryString)
0691: throws TorqueException {
0692: return executeQuery(queryString, Torque.getDefaultDB(), false);
0693: }
0694:
0695: /**
0696: * Utility method which executes a given sql statement. This
0697: * method should be used for select statements only. Use
0698: * executeStatement for update, insert, and delete operations.
0699: *
0700: * @param queryString A String with the sql statement to execute.
0701: * @param dbName The database to connect to.
0702: * @return List of Record objects.
0703: * @throws TorqueException Any exceptions caught during processing will be
0704: * rethrown wrapped into a TorqueException.
0705: */
0706: public static List executeQuery(String queryString, String dbName)
0707: throws TorqueException {
0708: return executeQuery(queryString, dbName, false);
0709: }
0710:
0711: /**
0712: * Method for performing a SELECT. Returns all results.
0713: *
0714: * @param queryString A String with the sql statement to execute.
0715: * @param dbName The database to connect to.
0716: * @param singleRecord Whether or not we want to select only a
0717: * single record.
0718: * @return List of Record objects.
0719: * @throws TorqueException Any exceptions caught during processing will be
0720: * rethrown wrapped into a TorqueException.
0721: */
0722: public static List executeQuery(String queryString, String dbName,
0723: boolean singleRecord) throws TorqueException {
0724: return executeQuery(queryString, 0, -1, dbName, singleRecord);
0725: }
0726:
0727: /**
0728: * Method for performing a SELECT. Returns all results.
0729: *
0730: * @param queryString A String with the sql statement to execute.
0731: * @param singleRecord Whether or not we want to select only a
0732: * single record.
0733: * @param con A Connection.
0734: * @return List of Record objects.
0735: * @throws TorqueException Any exceptions caught during processing will be
0736: * rethrown wrapped into a TorqueException.
0737: */
0738: public static List executeQuery(String queryString,
0739: boolean singleRecord, Connection con)
0740: throws TorqueException {
0741: return executeQuery(queryString, 0, -1, singleRecord, con);
0742: }
0743:
0744: /**
0745: * Method for performing a SELECT.
0746: *
0747: * @param queryString A String with the sql statement to execute.
0748: * @param start The first row to return.
0749: * @param numberOfResults The number of rows to return.
0750: * @param dbName The database to connect to.
0751: * @param singleRecord Whether or not we want to select only a
0752: * single record.
0753: * @return List of Record objects.
0754: * @throws TorqueException Any exceptions caught during processing will be
0755: * rethrown wrapped into a TorqueException.
0756: */
0757: public static List executeQuery(String queryString, int start,
0758: int numberOfResults, String dbName, boolean singleRecord)
0759: throws TorqueException {
0760: Connection con = null;
0761: List results = null;
0762: try {
0763: con = Torque.getConnection(dbName);
0764: // execute the query
0765: results = executeQuery(queryString, start, numberOfResults,
0766: singleRecord, con);
0767: } finally {
0768: Torque.closeConnection(con);
0769: }
0770: return results;
0771: }
0772:
0773: /**
0774: * Method for performing a SELECT. Returns all results.
0775: *
0776: * @param queryString A String with the sql statement to execute.
0777: * @param start The first row to return.
0778: * @param numberOfResults The number of rows to return.
0779: * @param singleRecord Whether or not we want to select only a
0780: * single record.
0781: * @param con A Connection.
0782: * @return List of Record objects.
0783: * @throws TorqueException Any exceptions caught during processing will be
0784: * rethrown wrapped into a TorqueException.
0785: */
0786: public static List executeQuery(String queryString, int start,
0787: int numberOfResults, boolean singleRecord, Connection con)
0788: throws TorqueException {
0789: QueryDataSet qds = null;
0790: List results = Collections.EMPTY_LIST;
0791: try {
0792: // execute the query
0793: long startTime = System.currentTimeMillis();
0794: qds = new QueryDataSet(con, queryString);
0795: if (log.isDebugEnabled()) {
0796: log.debug("Elapsed time="
0797: + (System.currentTimeMillis() - startTime)
0798: + " ms");
0799: }
0800: results = getSelectResults(qds, start, numberOfResults,
0801: singleRecord);
0802: } catch (DataSetException e) {
0803: throwTorqueException(e);
0804: } catch (SQLException e) {
0805: throwTorqueException(e);
0806: } finally {
0807: VillageUtils.close(qds);
0808: }
0809: return results;
0810: }
0811:
0812: /**
0813: * Returns all records in a QueryDataSet as a List of Record
0814: * objects. Used for functionality like util.LargeSelect.
0815: *
0816: * @see #getSelectResults(QueryDataSet, int, int, boolean)
0817: * @param qds the QueryDataSet
0818: * @return a List of Record objects
0819: * @throws TorqueException Any exceptions caught during processing will be
0820: * rethrown wrapped into a TorqueException.
0821: */
0822: public static List getSelectResults(QueryDataSet qds)
0823: throws TorqueException {
0824: return getSelectResults(qds, 0, -1, false);
0825: }
0826:
0827: /**
0828: * Returns all records in a QueryDataSet as a List of Record
0829: * objects. Used for functionality like util.LargeSelect.
0830: *
0831: * @see #getSelectResults(QueryDataSet, int, int, boolean)
0832: * @param qds the QueryDataSet
0833: * @param singleRecord
0834: * @return a List of Record objects
0835: * @throws TorqueException Any exceptions caught during processing will be
0836: * rethrown wrapped into a TorqueException.
0837: */
0838: public static List getSelectResults(QueryDataSet qds,
0839: boolean singleRecord) throws TorqueException {
0840: return getSelectResults(qds, 0, -1, singleRecord);
0841: }
0842:
0843: /**
0844: * Returns numberOfResults records in a QueryDataSet as a List
0845: * of Record objects. Starting at record 0. Used for
0846: * functionality like util.LargeSelect.
0847: *
0848: * @see #getSelectResults(QueryDataSet, int, int, boolean)
0849: * @param qds the QueryDataSet
0850: * @param numberOfResults
0851: * @param singleRecord
0852: * @return a List of Record objects
0853: * @throws TorqueException Any exceptions caught during processing will be
0854: * rethrown wrapped into a TorqueException.
0855: */
0856: public static List getSelectResults(QueryDataSet qds,
0857: int numberOfResults, boolean singleRecord)
0858: throws TorqueException {
0859: List results = null;
0860: if (numberOfResults != 0) {
0861: results = getSelectResults(qds, 0, numberOfResults,
0862: singleRecord);
0863: }
0864: return results;
0865: }
0866:
0867: /**
0868: * Returns numberOfResults records in a QueryDataSet as a List
0869: * of Record objects. Starting at record start. Used for
0870: * functionality like util.LargeSelect.
0871: *
0872: * @param qds The <code>QueryDataSet</code> to extract results
0873: * from.
0874: * @param start The index from which to start retrieving
0875: * <code>Record</code> objects from the data set.
0876: * @param numberOfResults The number of results to return (or
0877: * <code> -1</code> for all results).
0878: * @param singleRecord Whether or not we want to select only a
0879: * single record.
0880: * @return A <code>List</code> of <code>Record</code> objects.
0881: * @exception TorqueException If any <code>Exception</code> occurs.
0882: */
0883: public static List getSelectResults(QueryDataSet qds, int start,
0884: int numberOfResults, boolean singleRecord)
0885: throws TorqueException {
0886: List results = null;
0887: try {
0888: if (numberOfResults < 0) {
0889: results = new ArrayList();
0890: qds.fetchRecords();
0891: } else {
0892: results = new ArrayList(numberOfResults);
0893: qds.fetchRecords(start, numberOfResults);
0894: }
0895:
0896: int startRecord = 0;
0897:
0898: //Offset the correct number of records
0899: if (start > 0 && numberOfResults <= 0) {
0900: startRecord = start;
0901: }
0902:
0903: // Return a List of Record objects.
0904: for (int i = startRecord; i < qds.size(); i++) {
0905: Record rec = qds.getRecord(i);
0906: results.add(rec);
0907: }
0908:
0909: if (results.size() > 1 && singleRecord) {
0910: handleMultipleRecords(qds);
0911: }
0912: } catch (Exception e) {
0913: throwTorqueException(e);
0914: }
0915: return results;
0916: }
0917:
0918: /**
0919: * Helper method which returns the primary key contained
0920: * in the given Criteria object.
0921: *
0922: * @param criteria A Criteria.
0923: * @return ColumnMap if the Criteria object contains a primary
0924: * key, or null if it doesn't.
0925: * @throws TorqueException Any exceptions caught during processing will be
0926: * rethrown wrapped into a TorqueException.
0927: */
0928: private static ColumnMap getPrimaryKey(Criteria criteria)
0929: throws TorqueException {
0930: // Assume all the keys are for the same table.
0931: String key = (String) criteria.keys().nextElement();
0932:
0933: String table = criteria.getTableName(key);
0934: ColumnMap pk = null;
0935:
0936: if (!table.equals("")) {
0937: DatabaseMap dbMap = Torque.getDatabaseMap(criteria
0938: .getDbName());
0939: if (dbMap == null) {
0940: throw new TorqueException("dbMap is null");
0941: }
0942: if (dbMap.getTable(table) == null) {
0943: throw new TorqueException("dbMap.getTable() is null");
0944: }
0945:
0946: ColumnMap[] columns = dbMap.getTable(table).getColumns();
0947:
0948: for (int i = 0; i < columns.length; i++) {
0949: if (columns[i].isPrimaryKey()) {
0950: pk = columns[i];
0951: break;
0952: }
0953: }
0954: }
0955: return pk;
0956: }
0957:
0958: /**
0959: * Convenience method used to update rows in the DB. Checks if a
0960: * <i>single</i> int primary key is specified in the Criteria
0961: * object and uses it to perform the udpate. If no primary key is
0962: * specified an Exception will be thrown.
0963: * <p>
0964: * Use this method for performing an update of the kind:
0965: * <p>
0966: * "WHERE primary_key_id = an int"
0967: * <p>
0968: * To perform an update with non-primary key fields in the WHERE
0969: * clause use doUpdate(criteria, criteria).
0970: *
0971: * @param updateValues A Criteria object containing values used in
0972: * set clause.
0973: * @throws TorqueException Any exceptions caught during processing will be
0974: * rethrown wrapped into a TorqueException.
0975: */
0976: public static void doUpdate(Criteria updateValues)
0977: throws TorqueException {
0978: Connection con = null;
0979: try {
0980: con = Transaction.beginOptional(updateValues.getDbName(),
0981: updateValues.isUseTransaction());
0982: doUpdate(updateValues, con);
0983: Transaction.commit(con);
0984: } catch (TorqueException e) {
0985: Transaction.safeRollback(con);
0986: throw e;
0987: }
0988: }
0989:
0990: /**
0991: * Convenience method used to update rows in the DB. Checks if a
0992: * <i>single</i> int primary key is specified in the Criteria
0993: * object and uses it to perform the udpate. If no primary key is
0994: * specified an Exception will be thrown.
0995: * <p>
0996: * Use this method for performing an update of the kind:
0997: * <p>
0998: * "WHERE primary_key_id = an int"
0999: * <p>
1000: * To perform an update with non-primary key fields in the WHERE
1001: * clause use doUpdate(criteria, criteria).
1002: *
1003: * @param updateValues A Criteria object containing values used in
1004: * set clause.
1005: * @param con A Connection.
1006: * @throws TorqueException Any exceptions caught during processing will be
1007: * rethrown wrapped into a TorqueException.
1008: */
1009: public static void doUpdate(Criteria updateValues, Connection con)
1010: throws TorqueException {
1011: ColumnMap pk = getPrimaryKey(updateValues);
1012: Criteria selectCriteria = null;
1013:
1014: if (pk != null
1015: && updateValues.containsKey(pk.getFullyQualifiedName())) {
1016: selectCriteria = new Criteria(2);
1017: selectCriteria.put(pk.getFullyQualifiedName(), updateValues
1018: .remove(pk.getFullyQualifiedName()));
1019: } else {
1020: throw new TorqueException(
1021: "No PK specified for database update");
1022: }
1023:
1024: doUpdate(selectCriteria, updateValues, con);
1025: }
1026:
1027: /**
1028: * Method used to update rows in the DB. Rows are selected based
1029: * on selectCriteria and updated using values in updateValues.
1030: * <p>
1031: * Use this method for performing an update of the kind:
1032: * <p>
1033: * WHERE some_column = some value AND could_have_another_column =
1034: * another value AND so on...
1035: *
1036: * @param selectCriteria A Criteria object containing values used in where
1037: * clause.
1038: * @param updateValues A Criteria object containing values used in set
1039: * clause.
1040: * @throws TorqueException Any exceptions caught during processing will be
1041: * rethrown wrapped into a TorqueException.
1042: */
1043: public static void doUpdate(Criteria selectCriteria,
1044: Criteria updateValues) throws TorqueException {
1045: Connection con = null;
1046: try {
1047: con = Transaction.beginOptional(selectCriteria.getDbName(),
1048: updateValues.isUseTransaction());
1049: doUpdate(selectCriteria, updateValues, con);
1050: Transaction.commit(con);
1051: } catch (TorqueException e) {
1052: Transaction.safeRollback(con);
1053: throw e;
1054: }
1055: }
1056:
1057: /**
1058: * Method used to update rows in the DB. Rows are selected based
1059: * on criteria and updated using values in updateValues.
1060: * <p>
1061: * Use this method for performing an update of the kind:
1062: * <p>
1063: * WHERE some_column = some value AND could_have_another_column =
1064: * another value AND so on.
1065: *
1066: * @param criteria A Criteria object containing values used in where
1067: * clause.
1068: * @param updateValues A Criteria object containing values used in set
1069: * clause.
1070: * @param con A Connection.
1071: * @throws TorqueException Any exceptions caught during processing will be
1072: * rethrown wrapped into a TorqueException.
1073: */
1074: public static void doUpdate(Criteria criteria,
1075: final Criteria updateValues, Connection con)
1076: throws TorqueException {
1077: Set tables = SQLBuilder.getTableSet(criteria, null);
1078:
1079: try {
1080: processTables(criteria, tables, con, new ProcessCallback() {
1081: public void process(String table, String dbName,
1082: Record rec) throws Exception {
1083: // Callback must be called with table name without Schema!
1084: BasePeer.insertOrUpdateRecord(rec, table, dbName,
1085: updateValues);
1086: }
1087: });
1088: } catch (Exception e) {
1089: throwTorqueException(e);
1090: }
1091: }
1092:
1093: /**
1094: * Utility method which executes a given sql statement. This
1095: * method should be used for update, insert, and delete
1096: * statements. Use executeQuery() for selects.
1097: *
1098: * @param statementString A String with the sql statement to execute.
1099: * @return The number of rows affected.
1100: * @throws TorqueException Any exceptions caught during processing will be
1101: * rethrown wrapped into a TorqueException.
1102: */
1103: public static int executeStatement(String statementString)
1104: throws TorqueException {
1105: return executeStatement(statementString, Torque.getDefaultDB());
1106: }
1107:
1108: /**
1109: * Utility method which executes a given sql statement. This
1110: * method should be used for update, insert, and delete
1111: * statements. Use executeQuery() for selects.
1112: *
1113: * @param statementString A String with the sql statement to execute.
1114: * @param dbName Name of database to connect to.
1115: * @return The number of rows affected.
1116: * @throws TorqueException Any exceptions caught during processing will be
1117: * rethrown wrapped into a TorqueException.
1118: */
1119: public static int executeStatement(String statementString,
1120: String dbName) throws TorqueException {
1121: Connection con = null;
1122: int rowCount = -1;
1123: try {
1124: con = Torque.getConnection(dbName);
1125: rowCount = executeStatement(statementString, con);
1126: } finally {
1127: Torque.closeConnection(con);
1128: }
1129: return rowCount;
1130: }
1131:
1132: /**
1133: * Utility method which executes a given sql statement. This
1134: * method should be used for update, insert, and delete
1135: * statements. Use executeQuery() for selects.
1136: *
1137: * @param statementString A String with the sql statement to execute.
1138: * @param con A Connection.
1139: * @return The number of rows affected.
1140: * @throws TorqueException Any exceptions caught during processing will be
1141: * rethrown wrapped into a TorqueException.
1142: */
1143: public static int executeStatement(String statementString,
1144: Connection con) throws TorqueException {
1145: int rowCount = -1;
1146: Statement statement = null;
1147: try {
1148: statement = con.createStatement();
1149: rowCount = statement.executeUpdate(statementString);
1150: } catch (SQLException e) {
1151: throw new TorqueException(e);
1152: } finally {
1153: if (statement != null) {
1154: try {
1155: statement.close();
1156: } catch (SQLException e) {
1157: throw new TorqueException(e);
1158: }
1159: }
1160: }
1161: return rowCount;
1162: }
1163:
1164: /**
1165: * If the user specified that (s)he only wants to retrieve a
1166: * single record and multiple records are retrieved, this method
1167: * is called to handle the situation. The default behavior is to
1168: * throw an exception, but subclasses can override this method as
1169: * needed.
1170: *
1171: * @param ds The DataSet which contains multiple records.
1172: * @exception TorqueException Couldn't handle multiple records.
1173: */
1174: protected static void handleMultipleRecords(DataSet ds)
1175: throws TorqueException {
1176: throw new TorqueException(
1177: "Criteria expected single Record and "
1178: + "Multiple Records were selected");
1179: }
1180:
1181: /**
1182: * This method returns the MapBuilder specified in the name
1183: * parameter. You should pass in the full path to the class, ie:
1184: * org.apache.torque.util.db.map.TurbineMapBuilder. The
1185: * MapBuilder instances are cached in the TorqueInstance for speed.
1186: *
1187: * @param name name of the MapBuilder
1188: * @return A MapBuilder, not null
1189: * @throws TorqueException if the Map Builder cannot be instantiated
1190: * @deprecated Use Torque.getMapBuilder(name) instead
1191: */
1192: public static MapBuilder getMapBuilder(String name)
1193: throws TorqueException {
1194: return Torque.getMapBuilder(name);
1195: }
1196:
1197: /**
1198: * Performs a SQL <code>select</code> using a PreparedStatement.
1199: * Note: this method does not handle null criteria values.
1200: *
1201: * @param criteria
1202: * @param con
1203: * @return a List of Record objects.
1204: * @throws TorqueException Error performing database query.
1205: */
1206: public static List doPSSelect(Criteria criteria, Connection con)
1207: throws TorqueException {
1208: List v = null;
1209:
1210: StringBuffer qry = new StringBuffer();
1211: List params = new ArrayList(criteria.size());
1212:
1213: createPreparedStatement(criteria, qry, params);
1214:
1215: PreparedStatement statement = null;
1216: try {
1217: statement = con.prepareStatement(qry.toString());
1218:
1219: for (int i = 0; i < params.size(); i++) {
1220: Object param = params.get(i);
1221: if (param instanceof java.sql.Date) {
1222: statement.setDate(i + 1, (java.sql.Date) param);
1223: } else if (param instanceof NumberKey) {
1224: statement.setBigDecimal(i + 1, ((NumberKey) param)
1225: .getBigDecimal());
1226: } else if (param instanceof Integer) {
1227: statement.setInt(i + 1, ((Integer) param)
1228: .intValue());
1229: } else {
1230: statement.setString(i + 1, param.toString());
1231: }
1232: }
1233:
1234: QueryDataSet qds = null;
1235: try {
1236: qds = new QueryDataSet(statement.executeQuery());
1237: v = getSelectResults(qds);
1238: } finally {
1239: VillageUtils.close(qds);
1240: }
1241: } catch (DataSetException e) {
1242: throwTorqueException(e);
1243: } catch (SQLException e) {
1244: throwTorqueException(e);
1245: } finally {
1246: if (statement != null) {
1247: try {
1248: statement.close();
1249: } catch (SQLException e) {
1250: throw new TorqueException(e);
1251: }
1252: }
1253: }
1254: return v;
1255: }
1256:
1257: /**
1258: * Do a Prepared Statement select according to the given criteria
1259: *
1260: * @param criteria
1261: * @return a List of Record objects.
1262: * @throws TorqueException Any exceptions caught during processing will be
1263: * rethrown wrapped into a TorqueException.
1264: */
1265: public static List doPSSelect(Criteria criteria)
1266: throws TorqueException {
1267: Connection con = Torque.getConnection(criteria.getDbName());
1268: List v = null;
1269:
1270: try {
1271: v = doPSSelect(criteria, con);
1272: } finally {
1273: Torque.closeConnection(con);
1274: }
1275: return v;
1276: }
1277:
1278: /**
1279: * Create a new PreparedStatement. It builds a string representation
1280: * of a query and a list of PreparedStatement parameters.
1281: *
1282: * @param criteria
1283: * @param queryString
1284: * @param params
1285: * @throws TorqueException Any exceptions caught during processing will be
1286: * rethrown wrapped into a TorqueException.
1287: */
1288: public static void createPreparedStatement(Criteria criteria,
1289: StringBuffer queryString, List params)
1290: throws TorqueException {
1291: Query query = SQLBuilder.buildQueryClause(criteria, params,
1292: new SQLBuilder.QueryCallback() {
1293: public String process(Criteria.Criterion criterion,
1294: List params) {
1295: StringBuffer sb = new StringBuffer();
1296: criterion.appendPsTo(sb, params);
1297: return sb.toString();
1298: }
1299: });
1300:
1301: String sql = query.toString();
1302: log.debug(sql);
1303:
1304: queryString.append(sql);
1305: }
1306:
1307: /**
1308: * Checks all columns in the criteria to see whether
1309: * booleanchar and booleanint columns are queried with a boolean.
1310: * If yes, the query values are mapped onto values the database
1311: * does understand, i.e. 0 and 1 for booleanints and N and Y for
1312: * booleanchar columns.
1313: *
1314: * @param criteria The criteria to be checked for booleanint and booleanchar
1315: * columns.
1316: * @param defaultTableMap the table map to be used if the table name is
1317: * not given in a column.
1318: * @throws TorqueException if the database map for the criteria cannot be
1319: * retrieved.
1320: */
1321: public static void correctBooleans(Criteria criteria,
1322: TableMap defaultTableMap) throws TorqueException {
1323: Iterator keyIt = criteria.keySet().iterator();
1324: while (keyIt.hasNext()) {
1325: String key = (String) keyIt.next();
1326: String columnName;
1327: TableMap tableMap = null;
1328: int dotPosition = key.lastIndexOf(".");
1329: if (dotPosition == -1) {
1330: columnName = key;
1331: tableMap = defaultTableMap;
1332: } else {
1333: columnName = key.substring(dotPosition + 1);
1334: String tableName = key.substring(0, dotPosition);
1335: String databaseName = criteria.getDbName();
1336: if (databaseName == null) {
1337: databaseName = Torque.getDefaultDB();
1338: }
1339: DatabaseMap databaseMap = Torque
1340: .getDatabaseMap(databaseName);
1341: if (databaseMap != null) {
1342: tableMap = databaseMap.getTable(tableName);
1343: }
1344: if (tableMap == null) {
1345: // try aliases
1346: Map aliases = criteria.getAliases();
1347: if (aliases.get(tableName) != null) {
1348: tableName = (String) aliases.get(tableName);
1349: tableMap = databaseMap.getTable(tableName);
1350: }
1351: }
1352: }
1353: if (tableMap == null) {
1354: // no description of table available, do not modify anything
1355: continue;
1356: }
1357:
1358: ColumnMap columnMap = tableMap.getColumn(columnName);
1359: if (columnMap != null) {
1360: if ("BOOLEANINT".equals(columnMap.getTorqueType())) {
1361: Criteria.Criterion criterion = criteria
1362: .getCriterion(key);
1363: replaceBooleanValues(criterion, new Integer(1),
1364: new Integer(0));
1365: } else if ("BOOLEANCHAR".equals(columnMap
1366: .getTorqueType())) {
1367: Criteria.Criterion criterion = criteria
1368: .getCriterion(key);
1369: replaceBooleanValues(criterion, "Y", "N");
1370: }
1371: }
1372: }
1373: }
1374:
1375: /**
1376: * Replaces any Boolean value in the criterion and its attached Criterions
1377: * by trueValue if the Boolean equals <code>Boolean.TRUE</code>
1378: * and falseValue if the Boolean equals <code>Boolean.FALSE</code>.
1379: *
1380: * @param criterion the criterion to replace Boolean values in.
1381: * @param trueValue the value by which Boolean.TRUE should be replaced.
1382: * @param falseValue the value by which Boolean.FALSE should be replaced.
1383: */
1384: private static void replaceBooleanValues(
1385: Criteria.Criterion criterion, Object trueValue,
1386: Object falseValue) {
1387: // attachedCriterions also contains the criterion itself,
1388: // so no additional treatment is needed for the criterion itself.
1389: Criteria.Criterion[] attachedCriterions = criterion
1390: .getAttachedCriterion();
1391: for (int i = 0; i < attachedCriterions.length; ++i) {
1392: Object criterionValue = attachedCriterions[i].getValue();
1393: if (criterionValue instanceof Boolean) {
1394: Boolean booleanValue = (Boolean) criterionValue;
1395: attachedCriterions[i].setValue(Boolean.TRUE
1396: .equals(booleanValue) ? trueValue : falseValue);
1397: }
1398:
1399: }
1400:
1401: }
1402:
1403: /**
1404: * Process the result of a Table list generation.
1405: * This runs the statements onto the list of tables and
1406: * provides a callback hook to add functionality.
1407: *
1408: * This method should've been in SQLBuilder, but is uses the handleMultipleRecords callback thingie..
1409: *
1410: * @param crit The criteria
1411: * @param tables A set of Tables to run on
1412: * @param con The SQL Connection to run the statements on
1413: * @param pc A ProcessCallback object
1414: *
1415: * @throws Exception An Error occured (should be wrapped into TorqueException)
1416: */
1417: private static void processTables(Criteria crit, Set tables,
1418: Connection con, ProcessCallback pc) throws Exception {
1419: String dbName = crit.getDbName();
1420: DB db = Torque.getDB(dbName);
1421: DatabaseMap dbMap = Torque.getDatabaseMap(dbName);
1422:
1423: // create the statements for the tables
1424: for (Iterator it = tables.iterator(); it.hasNext();) {
1425: String table = (String) it.next();
1426: KeyDef kd = new KeyDef();
1427: Set whereClause = new HashSet();
1428:
1429: ColumnMap[] columnMaps = dbMap.getTable(table).getColumns();
1430:
1431: for (int j = 0; j < columnMaps.length; j++) {
1432: ColumnMap colMap = columnMaps[j];
1433: if (colMap.isPrimaryKey()) {
1434: kd.addAttrib(colMap.getColumnName());
1435: }
1436:
1437: String key = new StringBuffer(colMap.getTableName())
1438: .append('.').append(colMap.getColumnName())
1439: .toString();
1440:
1441: if (crit.containsKey(key)) {
1442: if (crit.getComparison(key).equals(Criteria.CUSTOM)) {
1443: whereClause.add(crit.getString(key));
1444: } else {
1445: whereClause.add(SqlExpression.build(colMap
1446: .getColumnName(), crit.getValue(key),
1447: crit.getComparison(key), crit
1448: .isIgnoreCase(), db));
1449: }
1450: }
1451: }
1452:
1453: // Execute the statement for each table
1454: TableDataSet tds = null;
1455: try {
1456: String tableName = SQLBuilder.getFullTableName(table,
1457: dbName);
1458:
1459: // Get affected records.
1460: tds = new TableDataSet(con, tableName, kd);
1461: String sqlSnippet = StringUtils.join(whereClause
1462: .iterator(), " AND ");
1463:
1464: if (log.isDebugEnabled()) {
1465: log.debug("BasePeer: whereClause=" + sqlSnippet);
1466: }
1467:
1468: tds.where(sqlSnippet);
1469: tds.fetchRecords();
1470:
1471: if (tds.size() > 1 && crit.isSingleRecord()) {
1472: handleMultipleRecords(tds);
1473: }
1474:
1475: for (int j = 0; j < tds.size(); j++) {
1476: Record rec = tds.getRecord(j);
1477:
1478: if (pc != null) {
1479: // Table name _without_ schema!
1480: pc.process(table, dbName, rec);
1481: }
1482: }
1483: } finally {
1484: VillageUtils.close(tds);
1485: }
1486: }
1487: }
1488:
1489: /**
1490: * Inner Interface that defines the Callback method for
1491: * the Record Processing
1492: */
1493: protected interface ProcessCallback {
1494: void process(String table, String dbName, Record rec)
1495: throws Exception;
1496: }
1497: }
|