Return-Path: Delivered-To: apmail-db-torque-dev-archive@www.apache.org Received: (qmail 85918 invoked from network); 27 Oct 2010 22:53:29 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 27 Oct 2010 22:53:29 -0000 Received: (qmail 90169 invoked by uid 500); 27 Oct 2010 22:53:29 -0000 Delivered-To: apmail-db-torque-dev-archive@db.apache.org Received: (qmail 90149 invoked by uid 500); 27 Oct 2010 22:53:29 -0000 Mailing-List: contact torque-dev-help@db.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "Apache Torque Developers List" Reply-To: "Apache Torque Developers List" Delivered-To: mailing list torque-dev@db.apache.org Received: (qmail 90141 invoked by uid 500); 27 Oct 2010 22:53:29 -0000 Received: (qmail 90138 invoked by uid 99); 27 Oct 2010 22:53:29 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 27 Oct 2010 22:53:29 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 27 Oct 2010 22:53:19 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 0BA8A23889E1; Wed, 27 Oct 2010 22:52:20 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1028130 [2/6] - in /db/torque/torque4/branches/trunk-without-village: torque-runtime/ torque-runtime/src/main/java/org/apache/torque/map/ torque-runtime/src/main/java/org/apache/torque/oid/ torque-runtime/src/main/java/org/apache/torque/om... Date: Wed, 27 Oct 2010 22:52:18 -0000 To: torque-commits@db.apache.org From: tfischer@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20101027225220.0BA8A23889E1@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: db/torque/torque4/branches/trunk-without-village/torque-runtime/src/main/java/org/apache/torque/util/BasePeer.java URL: http://svn.apache.org/viewvc/db/torque/torque4/branches/trunk-without-village/torque-runtime/src/main/java/org/apache/torque/util/BasePeer.java?rev=1028130&r1=1028129&r2=1028130&view=diff ============================================================================== --- db/torque/torque4/branches/trunk-without-village/torque-runtime/src/main/java/org/apache/torque/util/BasePeer.java (original) +++ db/torque/torque4/branches/trunk-without-village/torque-runtime/src/main/java/org/apache/torque/util/BasePeer.java Wed Oct 27 22:52:15 2010 @@ -22,10 +22,11 @@ package org.apache.torque.util; import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Types; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -39,7 +40,6 @@ import org.apache.torque.Database; import org.apache.torque.TooManyRowsException; import org.apache.torque.Torque; import org.apache.torque.TorqueException; -import org.apache.torque.adapter.DB; import org.apache.torque.map.ColumnMap; import org.apache.torque.map.DatabaseMap; import org.apache.torque.map.MapBuilder; @@ -49,15 +49,7 @@ import org.apache.torque.om.NumberKey; import org.apache.torque.om.ObjectKey; import org.apache.torque.om.SimpleKey; import org.apache.torque.om.StringKey; - -import com.workingdogs.village.Column; -import com.workingdogs.village.DataSet; -import com.workingdogs.village.DataSetException; -import com.workingdogs.village.KeyDef; -import com.workingdogs.village.QueryDataSet; -import com.workingdogs.village.Record; -import com.workingdogs.village.Schema; -import com.workingdogs.village.TableDataSet; +import org.apache.torque.om.mapper.RecordMapper; /** * This is the base class for all Peer classes in the system. Peer @@ -113,95 +105,6 @@ public abstract class BasePeer } /** - * Sets up a Schema for a table. This schema is then normally - * used as the argument for initTableColumns(). - * - * @param tableName The name of the table. - * @return A Schema. - */ - public static Schema initTableSchema(String tableName) - { - return initTableSchema(tableName, Torque.getDefaultDB()); - } - - /** - * Sets up a Schema for a table. This schema is then normally - * used as the argument for initTableColumns - * - * @param tableName The propery name for the database in the - * configuration file. - * @param dbName The name of the database. - * @return A Schema. - */ - public static Schema initTableSchema(String tableName, String dbName) - { - Schema schema = null; - Connection con = null; - - try - { - con = Torque.getConnection(dbName); - schema = new Schema().schema(con, tableName); - } - catch (Exception e) - { - log.error(e); - throw new Error("Error in BasePeer.initTableSchema(" - + tableName - + "): " - + e.getMessage()); - } - finally - { - Torque.closeConnection(con); - } - return schema; - } - - /** - * Creates a Column array for a table based on its Schema. - * - * @param schema A Schema object. - * @return A Column[]. - */ - public static Column[] initTableColumns(Schema schema) - { - Column[] columns = null; - try - { - int numberOfColumns = schema.numberOfColumns(); - columns = new Column[numberOfColumns]; - for (int i = 0; i < numberOfColumns; i++) - { - columns[i] = schema.column(i + 1); - } - } - catch (Exception e) - { - log.error(e); - throw new Error( - "Error in BasePeer.initTableColumns(): " + e.getMessage()); - } - return columns; - } - - /** - * Convenience method to create a String array of column names. - * - * @param columns A Column[]. - * @return A String[]. - */ - public static String[] initColumnNames(Column[] columns) - { - String[] columnNames = new String[columns.length]; - for (int i = 0; i < columns.length; i++) - { - columnNames[i] = columns[i].name().toUpperCase(); - } - return columnNames; - } - - /** * Convenience method to create a String array of criteria keys. * * @param tableName Name of table. @@ -368,227 +271,508 @@ public abstract class BasePeer /** * Method to perform deletes based on values and keys in a Criteria. * - * @param criteria The criteria to use. - * @param tableName the name of the table to delete records from. - * If set to null, the name of the table(s) can be extracted from - * the criteria, but this can cause unexpected results. - * @param con A Connection. + * @param criteria The criteria to select the records to be deleted, + * not null. + * @param tableName the name of the table to delete records from, not null. + * @param con The database connection for deletion, not null. + * * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ - public static void doDelete(Criteria criteria, String tableName, Connection con) - throws TorqueException + public static int doDelete( + Criteria criteria, + String tableName, + Connection connection) + throws TorqueException { - String dbName = criteria.getDbName(); - final DatabaseMap dbMap = Torque.getDatabaseMap(dbName); +// String dbName = criteria.getDbName(); +// final DatabaseMap dbMap = Torque.getDatabaseMap(dbName); // This Callback adds all tables to the Table set which // are referenced from a cascading criteria. As a result, all // data that is referenced through foreign keys will also be // deleted. - SQLBuilder.TableCallback tc = new SQLBuilder.TableCallback() { - public void process (Set tables, String key, Criteria crit) - { - if (crit.isCascade()) - { - // This steps thru all the columns in the database. - TableMap[] tableMaps = dbMap.getTables(); - for (int i = 0; i < tableMaps.length; i++) - { - ColumnMap[] columnMaps = tableMaps[i].getColumns(); - - for (int j = 0; j < columnMaps.length; j++) - { - // Only delete rows where the foreign key is - // also a primary key. Other rows need - // updating, but that is not implemented. - if (columnMaps[j].isForeignKey() - && columnMaps[j].isPrimaryKey() - && key.equals(columnMaps[j].getRelatedName())) - { - tables.add(tableMaps[i].getName()); - crit.add(columnMaps[j].getFullyQualifiedName(), - crit.getValue(key)); - } - } - } - } - } - }; +// TODO: reimplement +// SQLBuilder.TableCallback tc = new SQLBuilder.TableCallback() { +// public void process (Set tables, String key, Criteria crit) +// { +// if (crit.isCascade()) +// { +// // This steps thru all the columns in the database. +// TableMap[] tableMaps = dbMap.getTables(); +// for (int i = 0; i < tableMaps.length; i++) +// { +// ColumnMap[] columnMaps = tableMaps[i].getColumns(); +// +// for (int j = 0; j < columnMaps.length; j++) +// { +// // Only delete rows where the foreign key is +// // also a primary key. Other rows need +// // updating, but that is not implemented. +// if (columnMaps[j].isForeignKey() +// && columnMaps[j].isPrimaryKey() +// && key.equals(columnMaps[j].getRelatedName())) +// { +// tables.add(tableMaps[i].getName()); +// crit.add(columnMaps[j].getFullyQualifiedName(), +// crit.getValue(key)); +// } +// } +// } +// } +// } +// }; + + Query sql = createQuery(criteria); + + sql.setType(Query.Type.DELETE); + sql.getFromClause().clear(); + sql.getFromClause().add(new Query.FromElement(tableName, null, null)); - Set tables; - if (tableName == null) - { - tables = SQLBuilder.getTableSet(criteria, tc); - } - else + PreparedStatement preparedStatement = null; + try { - tables = new HashSet(1); - tables.add(tableName); - } + preparedStatement = connection.prepareStatement(sql.toString()); + long startTime = System.currentTimeMillis(); + log.debug("Executing delete " + sql.toString()); - try + int affectedRows = preparedStatement.executeUpdate(); + long queryEndTime = System.currentTimeMillis(); + log.trace("delete took " + (queryEndTime - startTime) + + " milliseconds"); + + preparedStatement.close(); + preparedStatement = null; + return affectedRows; + } + catch (SQLException e) { - processTables(criteria, tables, con, new ProcessCallback() { - public void process(String table, String dbName, Record rec) - throws Exception - { - rec.markToBeDeleted(); - rec.save(); - } - }); + throw new TorqueException(e); } - catch (Exception e) + finally { - throwTorqueException(e); + if (preparedStatement != null) + { + try + { + preparedStatement.close(); + } + catch (SQLException e) + { + log.warn("error closing prepared statement", e); + } + } } } /** - * Method to perform inserts based on values and keys in a - * Criteria. - *

- * If the primary key is auto incremented the data in Criteria - * will be inserted and the auto increment value will be returned. +// * Method to perform inserts based on values and keys in a +// * Criteria. +// *

+// * If the primary key is auto incremented the data in Criteria +// * will be inserted and the auto increment value will be returned. +// *

+// * If the primary key is included in Criteria then that value will +// * be used to insert the row. +// *

+// * If no primary key is included in Criteria then we will try to +// * figure out the primary key from the database map and insert the +// * row with the next available id using util.db.IDBroker. +// *

+// * If no primary key is defined for the table the values will be +// * inserted as specified in Criteria and -1 will be returned. +// * +// * @param criteria Object containing values to insert. +// * @return An Object which is the id of the row that was inserted +// * (if the table has a primary key) or null (if the table does not +// * have a primary key). +// * @throws TorqueException Any exceptions caught during processing will be +// * rethrown wrapped into a TorqueException. +// */ +// public static ObjectKey doInsert(Criteria criteria) throws TorqueException +// { +// Connection connection = null; +// try +// { +// connection = Transaction.beginOptional( +// criteria.getDbName(), +// criteria.isUseTransaction()); +// ObjectKey id = doInsert(criteria, connection); +// Transaction.commit(connection); +// connection = null; +// return id; +// } +// finally +// { +// if (connection != null) +// { +// Transaction.safeRollback(connection); +// } +// } +// } +// +// /** +// * Method to perform inserts based on values and keys in a +// * Criteria. +// *

+// * If the primary key is auto incremented the data in Criteria +// * will be inserted and the auto increment value will be returned. +// *

+// * If the primary key is included in Criteria then that value will +// * be used to insert the row. +// *

+// * If no primary key is included in Criteria then we will try to +// * figure out the primary key from the database map and insert the +// * row with the next available id using util.db.IDBroker. +// *

+// * If no primary key is defined for the table the values will be +// * inserted as specified in Criteria and null will be returned. +// * +// * @param criteria Object containing values to insert. +// * @param con A Connection. +// * @return An Object which is the id of the row that was inserted +// * (if the table has a primary key) or null (if the table does not +// * have a primary key). +// * @throws TorqueException Any exceptions caught during processing will be +// * rethrown wrapped into a TorqueException. +// */ +// public static ObjectKey doInsert(Criteria criteria, Connection con) +// throws TorqueException +// { +// SimpleKey id = null; +// +// // Get the table name and method for determining the primary +// // key value. +// String table = null; +// Iterator keys = criteria.keySet().iterator(); +// if (keys.hasNext()) +// { +// table = criteria.getTableName((String) keys.next()); +// } +// else +// { +// throw new TorqueException("Database insert attempted without " +// + "anything specified to insert"); +// } +// +// String dbName = criteria.getDbName(); +// Database database = Torque.getDatabase(dbName); +// DatabaseMap dbMap = database.getDatabaseMap(); +// TableMap tableMap = dbMap.getTable(table); +// Object keyInfo = tableMap.getPrimaryKeyMethodInfo(); +// IdGenerator keyGen +// = database.getIdGenerator(tableMap.getPrimaryKeyMethod()); +// +// ColumnMap pk = getPrimaryKey(criteria); +// +// // If the keyMethod is SEQUENCE or IDBROKERTABLE, get the id +// // before the insert. +// if (keyGen != null && keyGen.isPriorToInsert()) +// { +// // pk will be null if there is no primary key defined for the table +// // we're inserting into. +// if (pk != null && !criteria.containsKey(pk.getFullyQualifiedName())) +// { +// id = getId(pk, keyGen, con, keyInfo); +// criteria.add(pk.getFullyQualifiedName(), id.getValue()); +// } +// } +// +// List columnNames = new ArrayList(); +// List replacementObjects = new ArrayList(); +// for (Object updateValueObject : criteria.entrySet()) +// { +// Map.Entry updateValue = (Map.Entry) updateValueObject; +// String columnName = updateValue.getKey().toString(); +// if (columnName.lastIndexOf(".") != -1) +// { +// columnName = columnName.substring( +// columnName.lastIndexOf(".") + 1); +// } +// columnNames.add(columnName); +// replacementObjects.add( +// ((Criteria.Criterion) updateValue.getValue()).getValue()); +// } +// +// StringBuilder query = new StringBuilder("INSERT INTO ") +// .append(table) +// .append("(") +// .append(StringUtils.join(columnNames, ",")) +// .append(") VALUES ("); +// for (int i = 0; i < columnNames.size(); ++i) +// { +// if (i != 0) +// { +// query.append(","); +// } +// query.append("?"); +// } +// query.append(")"); +// +// PreparedStatement preparedStatement = null; +// try +// { +// preparedStatement = con.prepareStatement(query.toString()); +// int position = 1; +// for (Object replacementObject : replacementObjects) +// { +// preparedStatement.setObject(position, replacementObject); +// position++; +// } +// long startTime = System.currentTimeMillis(); +// log.debug("Executing insert " + query.toString() +// + " using parameters " + replacementObjects); +// +// preparedStatement.executeUpdate(); +// long queryEndTime = System.currentTimeMillis(); +// log.trace("insert took " + (queryEndTime - startTime) +// + " milliseconds"); +// +// preparedStatement.close(); +// preparedStatement = null; +// } +// catch (SQLException e) +// { +// throw new TorqueException(e); +// } +// finally +// { +// if (preparedStatement != null) +// { +// try +// { +// preparedStatement.close(); +// } +// catch (SQLException e) +// { +// log.warn("error closing prepared statement", e); +// } +// } +// } +// +// // If the primary key column is auto-incremented, get the id +// // now. +// if (keyGen != null && keyGen.isPostInsert()) +// { +// id = getId(pk, keyGen, con, keyInfo); +// } +// +// return id; +// } + + /** + * Inserts a record into a database table. *

- * If the primary key is included in Criteria then that value will + * If the primary key is included in Criteria, then that value will * be used to insert the row. *

- * If no primary key is included in Criteria then we will try to - * figure out the primary key from the database map and insert the - * row with the next available id using util.db.IDBroker. + * Otherwise, if the primary key can be generated automatically, + * the generated key will be used for the insert and will be returned. *

- * If no primary key is defined for the table the values will be - * inserted as specified in Criteria and -1 will be returned. + * If no value is given for the primary key is defined and it cannot + * be generated automatically or the table has no primary key, + * the values will be inserted as specified and null will be returned. * - * @param criteria Object containing values to insert. - * @return An Object which is the id of the row that was inserted - * (if the table has a primary key) or null (if the table does not - * have a primary key). - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. + * @param insertValues Contains the values to insert, not null. + * + * @return the primary key of the inserted row (if the table + * has a primary key) or null (if the table does not have + * a primary key). + * + * @throws TorqueException if a database error occurs. */ - public static ObjectKey doInsert(Criteria criteria) throws TorqueException + public static ObjectKey doInsert(ColumnValues insertValues) + throws TorqueException { - Connection con = null; - ObjectKey id = null; - + String databaseName; + { + TableMap table = insertValues.getTable(); + if (table == null) + { + log.debug("doUpdate(): no Table set in updateValues, " + + "using default database"); + databaseName = Torque.getDefaultDB(); + } + else + { + databaseName = table.getDatabaseMap().getName(); + } + } + Connection connection = null; try { - con = Transaction.beginOptional( - criteria.getDbName(), - criteria.isUseTransaction()); - id = doInsert(criteria, con); - Transaction.commit(con); + connection = Transaction.begin(databaseName); + ObjectKey id = doInsert(insertValues, connection); + Transaction.commit(connection); + connection = null; + return id; } - catch (TorqueException e) + finally { - Transaction.safeRollback(con); - throw e; + if (connection != null) + { + Transaction.safeRollback(connection); + } } - - return id; } /** - * Method to perform inserts based on values and keys in a - * Criteria. + * Inserts a record into a database table. *

- * If the primary key is auto incremented the data in Criteria - * will be inserted and the auto increment value will be returned. - *

- * If the primary key is included in Criteria then that value will + * If the primary key is included in Criteria, then that value will * be used to insert the row. *

- * If no primary key is included in Criteria then we will try to - * figure out the primary key from the database map and insert the - * row with the next available id using util.db.IDBroker. + * Otherwise, if the primary key can be generated automatically, + * the generated key will be used for the insert and will be returned. *

- * If no primary key is defined for the table the values will be - * inserted as specified in Criteria and null will be returned. - * - * @param criteria Object containing values to insert. - * @param con A Connection. - * @return An Object which is the id of the row that was inserted - * (if the table has a primary key) or null (if the table does not - * have a primary key). - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static ObjectKey doInsert(Criteria criteria, Connection con) - throws TorqueException + * If no value is given for the primary key is defined and it cannot + * be generated automatically or the table has no primary key, + * the values will be inserted as specified and null will be returned. + * + * @param insertValues Contains the values to insert, not null. + * @param connection the connection to use for the insert, not null. + * + * @return the primary key of the inserted row (if the table + * has a primary key) or null (if the table does not have + * a primary key). + * + * @throws TorqueException if a database error occurs. + */ + public static ObjectKey doInsert( + ColumnValues insertValues, + Connection connection) + throws TorqueException { - SimpleKey id = null; - - // Get the table name and method for determining the primary - // key value. - String table = null; - Iterator keys = criteria.keySet().iterator(); - if (keys.hasNext()) - { - table = criteria.getTableName((String) keys.next()); - } - else - { - throw new TorqueException("Database insert attempted without " - + "anything specified to insert"); - } - - String dbName = criteria.getDbName(); - Database database = Torque.getDatabase(dbName); - DatabaseMap dbMap = database.getDatabaseMap(); - TableMap tableMap = dbMap.getTable(table); + TableMap tableMap = insertValues.getTable(); + DatabaseMap dbMap = tableMap.getDatabaseMap(); + Database database = Torque.getDatabase(dbMap.getName()); Object keyInfo = tableMap.getPrimaryKeyMethodInfo(); IdGenerator keyGen = database.getIdGenerator(tableMap.getPrimaryKeyMethod()); - ColumnMap pk = getPrimaryKey(criteria); - - // If the keyMethod is SEQUENCE or IDBROKERTABLE, get the id - // before the insert. - if (keyGen != null && keyGen.isPriorToInsert()) + SimpleKey id = null; + // can currently generate only single column pks, therefore a single + // columnMap is ok + ColumnMap primaryKey = null; + if (keyGen != null) { - // pk will be null if there is no primary key defined for the table - // we're inserting into. - if (pk != null && !criteria.containsKey(pk.getFullyQualifiedName())) + // fail on multiple pks + primaryKey = tableMap.getPrimaryKey(); + + // primaryKey will be null if there is no primary key + // defined for the table we're inserting into. + if (keyGen.isPriorToInsert() && primaryKey != null + && !insertValues.containsKey( + primaryKey.getFullyQualifiedName())) { - id = getId(pk, keyGen, con, keyInfo); - criteria.add(pk.getFullyQualifiedName(), id); + id = getId(primaryKey, keyGen, connection, keyInfo); + insertValues.put( + primaryKey.getFullyQualifiedName(), + new JdbcTypedValue(id.getValue(), id.getJdbcType())); } } - // Use Village to perform the insert. - TableDataSet tds = null; - try + List columnNames = new ArrayList(); + List replacementObjects + = new ArrayList(); + for (Map.Entry columnValue + : insertValues.entrySet()) { - String tableName = SQLBuilder.getFullTableName(table, dbName); - tds = new TableDataSet(con, tableName); - Record rec = tds.addRecord(); - // not the fully qualified name, insertOrUpdateRecord wants to use table as an index... - BasePeer.insertOrUpdateRecord(rec, table, dbName, criteria); + String columnName = columnValue.getKey(); + if (columnName.lastIndexOf(".") != -1) + { + columnName = columnName.substring( + columnName.lastIndexOf(".") + 1); + } + columnNames.add(columnName); + JdbcTypedValue value = columnValue.getValue(); + replacementObjects.add(value); } - catch (DataSetException e) + + StringBuilder query = new StringBuilder("INSERT INTO ") + .append(tableMap.getName()) + .append("(") + .append(StringUtils.join(columnNames, ",")) + .append(") VALUES ("); + for (int i = 0; i < columnNames.size(); ++i) { - throwTorqueException(e); + if (i != 0) + { + query.append(","); + } + query.append("?"); } - catch (SQLException e) + query.append(")"); + + PreparedStatement preparedStatement = null; + try { - throwTorqueException(e); + preparedStatement = connection.prepareStatement(query.toString()); + int position = 1; + for (JdbcTypedValue replacementObject : replacementObjects) + { + Object value = replacementObject.getValue(); + if (value != null) + { + if (replacementObject.getJdbcType() != Types.BLOB + && replacementObject.getJdbcType() != Types.CLOB) + { + preparedStatement.setObject( + position, + value, + replacementObject.getJdbcType()); + } + else + { + preparedStatement.setObject( + position, + value); + } + } + else + { + preparedStatement.setNull( + position, + replacementObject.getJdbcType()); + } + position++; + } + long startTime = System.currentTimeMillis(); + log.debug("Executing insert " + query.toString() + + " using parameters " + replacementObjects); + + preparedStatement.executeUpdate(); + long queryEndTime = System.currentTimeMillis(); + log.trace("insert took " + (queryEndTime - startTime) + + " milliseconds"); + + preparedStatement.close(); + preparedStatement = null; } - catch (TorqueException e) + catch (SQLException e) { - throwTorqueException(e); + throw new TorqueException(e); } finally { - VillageUtils.close(tds); + if (preparedStatement != null) + { + try + { + preparedStatement.close(); + } + catch (SQLException e) + { + log.warn("error closing prepared statement", e); + } + } } // If the primary key column is auto-incremented, get the id // now. if (keyGen != null && keyGen.isPostInsert()) { - id = getId(pk, keyGen, con, keyInfo); + id = getId(primaryKey, keyGen, connection, keyInfo); } return id; @@ -633,66 +817,6 @@ public abstract class BasePeer } /** - * Grouping of code used in both doInsert() and doUpdate() - * methods. Sets up a Record for saving. - * - * @param rec A Record. - * @param table Name of table. - * @param criteria A Criteria. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - private static void insertOrUpdateRecord( - Record rec, - String table, - String dbName, - Criteria criteria) - throws TorqueException - { - DatabaseMap dbMap = Torque.getDatabaseMap(dbName); - - ColumnMap[] columnMaps = dbMap.getTable(table).getColumns(); - boolean shouldSave = false; - for (int j = 0; j < columnMaps.length; j++) - { - ColumnMap colMap = columnMaps[j]; - String colName = colMap.getColumnName(); - String key = new StringBuffer(colMap.getTableName()) - .append('.') - .append(colName) - .toString(); - if (criteria.containsKey(key)) - { - try - { - VillageUtils.setVillageValue(criteria, key, rec, colName); - shouldSave = true; - } - catch (Exception e) - { - throwTorqueException(e); - } - } - } - - if (shouldSave) - { - try - { - rec.save(); - } - catch (Exception e) - { - throwTorqueException(e); - } - } - else - { - throw new TorqueException("No changes to save"); - } - } - - /** * Method to create an SQL query for display only based on values in a * Criteria. * @@ -742,452 +866,664 @@ public abstract class BasePeer } /** - * Returns all results. + * Selects rows from a database an maps them to objects. * - * @param criteria A Criteria. - * @return List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List doSelectVillageRecords( - Criteria criteria) + * @param criteria A Criteria specifying the records to select, not null. + * @param mapper The mapper creating the objects from the resultSet, + * not null. + * @param defaultTableMap The table map used for the + * unqualified columns in the query, not null. + * + * @return The results of the query, not null. + * + * @throws TorqueException if querying the database fails. + */ + public static List doSelect( + Criteria criteria, + RecordMapper mapper, + TableMap defaultTableMap) throws TorqueException { - Connection con = null; - List results = null; - + Connection connection = null; try { - con = Transaction.beginOptional( + connection = Transaction.beginOptional( criteria.getDbName(), criteria.isUseTransaction()); - results = doSelectVillageRecords(criteria, con); - Transaction.commit(con); + + List result = doSelect( + criteria, + mapper, + defaultTableMap, + connection); + + Transaction.commit(connection); + connection = null; + return result; } - catch (TorqueException e) + finally { - Transaction.safeRollback(con); - throw e; + if (connection != null) + { + Transaction.safeRollback(connection); + } } - return results; } /** - * Returns all results. + * Selects rows from a database an maps them to objects. * - * @param criteria A Criteria. - * @param con A Connection. - * @return List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. + * @param query the sql query to execute, not null. + * @param mapper The mapper creating the objects from the resultSet, + * not null. + * @param defaultTableMap The table map used for the + * unqualified columns in the query, not null. + * @param dbName The name of the database to create the connection for, + * or null for the default SDB. + * + * @return The results of the query, not null. + * + * @throws TorqueException if querying the database fails. */ - public static List doSelectVillageRecords( - Criteria criteria, Connection con) + public static List doSelect( + String query, + RecordMapper mapper, + TableMap defaultTableMap, + String dbName) throws TorqueException { - Query query = createQuery(criteria); - DB dbadapter = Torque.getDB(criteria.getDbName()); - - // Call Village depending on the capabilities of the DB - return executeQuery(query.toString(), - dbadapter.supportsNativeOffset() ? 0 : criteria.getOffset(), - dbadapter.supportsNativeLimit() ? -1 : criteria.getLimit(), - criteria.isSingleRecord(), - con); + if (dbName == null) + { + dbName = Torque.getDefaultDB(); + } + Connection connection = null; + try + { + connection = Transaction.beginOptional( + dbName, + true); + + List result = doSelect( + query, + mapper, + defaultTableMap, + connection); + + Transaction.commit(connection); + connection = null; + return result; + } + finally + { + if (connection != null) + { + Transaction.safeRollback(connection); + } + } } /** - * Utility method which executes a given sql statement. This - * method should be used for select statements only. Use - * executeStatement for update, insert, and delete operations. + * Selects rows from a database an maps them to objects. * - * @param queryString A String with the sql statement to execute. - * @return List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List executeQuery(String queryString) throws TorqueException - { - return executeQuery(queryString, Torque.getDefaultDB(), false); - } - - /** - * Utility method which executes a given sql statement. This - * method should be used for select statements only. Use - * executeStatement for update, insert, and delete operations. + * @param criteria A Criteria specifying the records to select, not null. + * @param mapper The mapper creating the objects from the resultSet, + * not null. + * @param defaultTableMap The table map used for the + * unqualified columns in the query, not null. + * @param connection the database connection, not null. * - * @param queryString A String with the sql statement to execute. - * @param dbName The database to connect to. - * @return List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List executeQuery(String queryString, String dbName) - throws TorqueException - { - return executeQuery(queryString, dbName, false); - } - - /** - * Method for performing a SELECT. Returns all results. + * @return The results of the query, not null. * - * @param queryString A String with the sql statement to execute. - * @param dbName The database to connect to. - * @param singleRecord Whether or not we want to select only a - * single record. - * @return List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. + * @throws TorqueException if querying the database fails. */ - public static List executeQuery( - String queryString, - String dbName, - boolean singleRecord) - throws TorqueException + public static List doSelect( + Criteria criteria, + RecordMapper mapper, + TableMap defaultTableMap, + Connection connection) + throws TorqueException { - return executeQuery(queryString, 0, -1, dbName, singleRecord); - } - - /** - * Method for performing a SELECT. Returns all results. - * - * @param queryString A String with the sql statement to execute. - * @param singleRecord Whether or not we want to select only a - * single record. - * @param con A Connection. - * @return List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List executeQuery( - String queryString, - boolean singleRecord, - Connection con) - throws TorqueException - { - return executeQuery(queryString, 0, -1, singleRecord, con); - } - - /** - * Method for performing a SELECT. - * - * @param queryString A String with the sql statement to execute. - * @param start The first row to return. - * @param numberOfResults The number of rows to return. - * @param dbName The database to connect to. - * @param singleRecord Whether or not we want to select only a - * single record. - * @return List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List executeQuery( - String queryString, - int start, - int numberOfResults, - String dbName, - boolean singleRecord) - throws TorqueException - { - Connection con = null; - List results = null; - try - { - con = Torque.getConnection(dbName); - // execute the query - results = executeQuery( - queryString, - start, - numberOfResults, - singleRecord, - con); - } - finally + if (connection == null) { - Torque.closeConnection(con); + throw new NullPointerException("connection is null"); } - return results; - } - /** - * Method for performing a SELECT. Returns all results. - * - * @param queryString A String with the sql statement to execute. - * @param start The first row to return. - * @param numberOfResults The number of rows to return. - * @param singleRecord Whether or not we want to select only a - * single record. - * @param con A Connection. - * @return List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List executeQuery( - String queryString, - int start, - int numberOfResults, - boolean singleRecord, - Connection con) - throws TorqueException - { - QueryDataSet qds = null; - List results = Collections.emptyList(); + correctBooleans(criteria, defaultTableMap); + + Database database = Torque.getDatabase(criteria.getDbName()); + String query = createQuery(criteria).toString(); + + Statement statement = null; + ResultSet resultSet = null; try { - // execute the query + statement = connection.createStatement(); long startTime = System.currentTimeMillis(); - qds = new QueryDataSet(con, queryString); - if (log.isDebugEnabled()) + log.debug("Executing query " + query); + + resultSet = statement.executeQuery(query.toString()); + long queryEndTime = System.currentTimeMillis(); + log.trace("query took " + (queryEndTime - startTime) + + " milliseconds"); + + int offset; + if (database.getAdapter().supportsNativeOffset()) { - log.debug("Elapsed time=" - + (System.currentTimeMillis() - startTime) + " ms"); + offset = 0; //database takes care of offset } - results = getSelectResults( - qds, start, numberOfResults, singleRecord); - } - catch (DataSetException e) - { - throwTorqueException(e); + else + { + offset = criteria.getOffset(); + } + + int limit; + if (database.getAdapter().supportsNativeLimit()) + { + limit = -1; //database takes care of offset + } + else + { + if (database.getAdapter().supportsNativeOffset()) + { + limit = criteria.getLimit(); + } + else + { + if (criteria.getLimit() == -1 ) + { + limit = criteria.getLimit(); + } + else + { + limit = offset + criteria.getLimit(); + } + } + } + + List result = new ArrayList(); + int rowNumber = 0; + while (resultSet.next()) + { + if (rowNumber < offset) + { + rowNumber++; + continue; + } + if (limit >= 0 && rowNumber >= limit) + { + break; + } + + T rowResult = mapper.processRow(resultSet, 0); + result.add(rowResult); + + rowNumber++; + } + long mappingEndTime = System.currentTimeMillis(); + log.trace("mapping took " + (mappingEndTime - queryEndTime) + + " milliseconds"); + + if (criteria.isSingleRecord() && result.size() > 1) + { + throw new TooManyRowsException("Criteria expected single Record and " + + "Multiple Records were selected"); + } + return result; } catch (SQLException e) { - throwTorqueException(e); + throw new TorqueException(e); } finally { - VillageUtils.close(qds); + if (resultSet != null) + { + try + { + resultSet.close(); + } + catch (SQLException e) + { + log.warn("error closing resultSet", e); + } + } + if (statement != null) + { + try + { + statement.close(); + } + catch (SQLException e) + { + log.warn("error closing statement", e); + } + } } - return results; } /** - * Returns all records in a QueryDataSet as a List of Record - * objects. Used for functionality like util.LargeSelect. + * Selects rows from a database an maps them to objects. * - * @see #getSelectResults(QueryDataSet, int, int, boolean) - * @param qds the QueryDataSet - * @return a List of Record objects - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List getSelectResults(QueryDataSet qds) - throws TorqueException - { - return getSelectResults(qds, 0, -1, false); - } - - /** - * Returns all records in a QueryDataSet as a List of Record - * objects. Used for functionality like util.LargeSelect. + * @param query the SQL Query to execute, not null. + * @param mapper The mapper creating the objects from the resultSet, + * not null. + * @param defaultTableMap The table map used for the + * unqualified columns in the query, not null. + * @param connection the database connection, not null. * - * @see #getSelectResults(QueryDataSet, int, int, boolean) - * @param qds the QueryDataSet - * @param singleRecord - * @return a List of Record objects - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List getSelectResults(QueryDataSet qds, boolean singleRecord) - throws TorqueException - { - return getSelectResults(qds, 0, -1, singleRecord); - } - - /** - * Returns numberOfResults records in a QueryDataSet as a List - * of Record objects. Starting at record 0. Used for - * functionality like util.LargeSelect. + * @return The results of the query, not null. * - * @see #getSelectResults(QueryDataSet, int, int, boolean) - * @param qds the QueryDataSet - * @param numberOfResults - * @param singleRecord - * @return a List of Record objects - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. + * @throws TorqueException if querying the database fails. */ - public static List getSelectResults( - QueryDataSet qds, - int numberOfResults, - boolean singleRecord) - throws TorqueException + public static List doSelect( + String query, + RecordMapper mapper, + TableMap defaultTableMap, + Connection connection) + throws TorqueException { - List results = null; - if (numberOfResults != 0) + if (connection == null) { - results = getSelectResults(qds, 0, numberOfResults, singleRecord); + throw new NullPointerException("connection is null"); } - return results; - } - /** - * Returns numberOfResults records in a QueryDataSet as a List - * of Record objects. Starting at record start. Used for - * functionality like util.LargeSelect. - * - * @param qds The QueryDataSet to extract results - * from. - * @param start The index from which to start retrieving - * Record objects from the data set. - * @param numberOfResults The number of results to return (or - * -1 for all results). - * @param singleRecord Whether or not we want to select only a - * single record. - * @return A List of Record objects. - * @exception TorqueException If any Exception occurs. - */ - public static List getSelectResults( - QueryDataSet qds, - int start, - int numberOfResults, - boolean singleRecord) - throws TorqueException - { - List results = null; + List result = new ArrayList(); + Statement statement = null; + ResultSet resultSet = null; try { - if (numberOfResults < 0) - { - results = new ArrayList(); - qds.fetchRecords(); - } - else - { - results = new ArrayList(numberOfResults); - qds.fetchRecords(start, numberOfResults); - } - - int startRecord = 0; - - //Offset the correct number of records - if (start > 0 && numberOfResults <= 0) - { - startRecord = start; - } + statement = connection.createStatement(); + long startTime = System.currentTimeMillis(); + log.debug("Executing query " + query); - // Return a List of Record objects. - for (int i = startRecord; i < qds.size(); i++) - { - Record rec = qds.getRecord(i); - results.add(rec); - } + resultSet = statement.executeQuery(query.toString()); + long queryEndTime = System.currentTimeMillis(); + log.trace("query took " + (queryEndTime - startTime) + + " milliseconds"); - if (results.size() > 1 && singleRecord) + while (resultSet.next()) { - handleMultipleRecords(qds); + T rowResult = mapper.processRow(resultSet, 0); + result.add(rowResult); } + long mappingEndTime = System.currentTimeMillis(); + log.trace("mapping took " + (mappingEndTime - queryEndTime) + + " milliseconds"); } - catch (Exception e) + catch (SQLException e) { - throwTorqueException(e); + throw new TorqueException(e); } - return results; - } - - /** - * Helper method which returns the primary key contained - * in the given Criteria object. - * - * @param criteria A Criteria. - * @return ColumnMap if the Criteria object contains a primary - * key, or null if it doesn't. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - private static ColumnMap getPrimaryKey(Criteria criteria) - throws TorqueException - { - // Assume all the keys are for the same table. - String key = (String) criteria.keys().nextElement(); - - String table = criteria.getTableName(key); - ColumnMap pk = null; - - if (!table.equals("")) + finally { - DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); - if (dbMap == null) - { - throw new TorqueException("dbMap is null"); - } - if (dbMap.getTable(table) == null) + if (resultSet != null) { - throw new TorqueException("dbMap.getTable() is null"); + try + { + resultSet.close(); + } + catch (SQLException e) + { + log.warn("error closing resultSet", e); + } } - - ColumnMap[] columns = dbMap.getTable(table).getColumns(); - - for (int i = 0; i < columns.length; i++) + if (statement != null) { - if (columns[i].isPrimaryKey()) + try { - pk = columns[i]; - break; + statement.close(); + } + catch (SQLException e) + { + log.warn("error closing statement", e); } } } - return pk; + return result; } +// /** +// * Helper method which returns the primary key contained +// * in the given Criteria object. +// * +// * @param criteria A Criteria. +// * @return ColumnMap if the Criteria object contains a primary +// * key, or null if it doesn't. +// * @throws TorqueException Any exceptions caught during processing will be +// * rethrown wrapped into a TorqueException. +// */ +// private static ColumnMap getPrimaryKey(Criteria criteria) +// throws TorqueException +// { +// // Assume all the keys are for the same table. +// String key = (String) criteria.keys().nextElement(); +// +// String table = criteria.getTableName(key); +// ColumnMap pk = null; +// +// if (!table.equals("")) +// { +// DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); +// if (dbMap == null) +// { +// throw new TorqueException("dbMap is null"); +// } +// if (dbMap.getTable(table) == null) +// { +// throw new TorqueException("dbMap.getTable() is null"); +// } +// +// ColumnMap[] columns = dbMap.getTable(table).getColumns(); +// +// for (int i = 0; i < columns.length; i++) +// { +// if (columns[i].isPrimaryKey()) +// { +// pk = columns[i]; +// break; +// } +// } +// } +// return pk; +// } +// +// /** +// * Convenience method used to update rows in the DB. Checks if a +// * single int primary key is specified in the Criteria +// * object and uses it to perform the udpate. If no primary key is +// * specified an Exception will be thrown. +// *

+// * Use this method for performing an update of the kind: +// *

+// * "WHERE primary_key_id = an int" +// *

+// * To perform an update with non-primary key fields in the WHERE +// * clause use doUpdate(criteria, criteria). +// * +// * @param updateValues A Criteria object containing values used in +// * set clause. +// * +// * @return the number of affected rows. +// * +// * @throws TorqueException Any exceptions caught during processing will be +// * rethrown wrapped into a TorqueException. +// */ +// public static int doUpdate(Criteria updateValues) throws TorqueException +// { +// Connection connection = null; +// try +// { +// connection = Transaction.beginOptional( +// updateValues.getDbName(), +// updateValues.isUseTransaction()); +// int result = doUpdate(updateValues, connection); +// Transaction.commit(connection); +// connection = null; +// return result; +// } +// finally +// { +// if (connection != null) +// { +// Transaction.safeRollback(connection); +// } +// } +// } +// +// /** +// * Convenience method used to update rows in the DB. Checks if a +// * single int primary key is specified in the Criteria +// * object and uses it to perform the udpate. If no primary key is +// * specified an Exception will be thrown. +// *

+// * Use this method for performing an update of the kind: +// *

+// * "WHERE primary_key_id = an int" +// *

+// * To perform an update with non-primary key fields in the WHERE +// * clause use doUpdate(criteria, criteria). +// * +// * @param updateValues A Criteria object containing values used in +// * set clause. +// * @param con A Connection. +// * +// * @return the number of affected rows. +// * +// * @throws TorqueException Any exceptions caught during processing will be +// * rethrown wrapped into a TorqueException. +// */ +// public static int doUpdate(Criteria updateValues, Connection con) +// throws TorqueException +// { +// ColumnMap pk = getPrimaryKey(updateValues); +// Criteria selectCriteria = null; +// +// if (pk != null && updateValues.containsKey(pk.getFullyQualifiedName())) +// { +// selectCriteria = new Criteria(2); +// selectCriteria.put(pk.getFullyQualifiedName(), +// updateValues.remove(pk.getFullyQualifiedName())); +// } +// else +// { +// throw new TorqueException("No PK specified for database update"); +// } +// +// return doUpdate(selectCriteria, updateValues, con); +// } +// +// /** +// * Executes an update against the database. The rows to be updated +// * are selected using criteria and updated using the values +// * in updateValues. +// * +// * @param criteria selects which rows of which table should be updated. +// * @param updateValues which columns(map keys) should be set to which +// * values. +// * @param connection the database connection to use, not null. +// * +// * @return the number of affected rows. +// * +// * @throws TorqueException if updating fails. +// */ +// public static void doUpdate( +// Criteria selectCriteria, +// Criteria updateValues) +// throws TorqueException +// { +// Connection con = null; +// try +// { +// con = Transaction.beginOptional( +// selectCriteria.getDbName(), +// selectCriteria.isUseTransaction()); +// doUpdate(selectCriteria, updateValues, con); +// Transaction.commit(con); +// } +// catch (TorqueException e) +// { +// Transaction.safeRollback(con); +// throw e; +// } +// } +// +// /** +// * Executes an update against the database. The rows to be updated +// * are selected using criteria and updated using the values +// * in updateValues. +// * +// * @param criteria selects which rows of which table should be updated. +// * @param updateValues which columns(map keys) should be set to which +// * values. +// * @param connection the database connection to use, not null. +// * +// * @return the number of affected rows. +// * +// * @throws TorqueException if updating fails. +// */ +// public static int doUpdate( +// Criteria criteria, +// Criteria updateValues, +// Connection connection) +// throws TorqueException +// { +// Query sql = createQuery(criteria); +// sql.setType(Query.Type.UPDATE); +// +// // ignore select columns in criteria +// sql.getSelectClause().clear(); +// List replacementObjects = new ArrayList(); +// for (Object updateValueObject : updateValues.entrySet()) +// { +// Map.Entry updateValue = (Map.Entry) updateValueObject; +// String columnName = updateValue.getKey().toString(); +// if (columnName.lastIndexOf(".") != -1) +// { +// columnName = columnName.substring( +// columnName.lastIndexOf(".") + 1); +// } +// sql.getSelectClause().add(columnName); +// replacementObjects.add( +// ((Criteria.Criterion) updateValue.getValue()).getValue()); +// } +// +// PreparedStatement preparedStatement = null; +// try +// { +// preparedStatement = connection.prepareStatement(sql.toString()); +// int position = 1; +// for (Object replacementObject : replacementObjects) +// { +// preparedStatement.setObject(position, replacementObject); +// position++; +// } +// long startTime = System.currentTimeMillis(); +// log.debug("Executing update " + sql.toString() +// + " using parameters " + replacementObjects); +// +// int affectedRows = preparedStatement.executeUpdate(); +// long queryEndTime = System.currentTimeMillis(); +// log.trace("update took " + (queryEndTime - startTime) +// + " milliseconds"); +// +// preparedStatement.close(); +// preparedStatement = null; +// return affectedRows; +// } +// catch (SQLException e) +// { +// throw new TorqueException(e); +// } +// finally +// { +// if (preparedStatement != null) +// { +// try +// { +// preparedStatement.close(); +// } +// catch (SQLException e) +// { +// log.warn("error closing prepared statement", e); +// } +// } +// } +// } + + /** * Convenience method used to update rows in the DB. Checks if a - * single int primary key is specified in the Criteria - * object and uses it to perform the udpate. If no primary key is - * specified an Exception will be thrown. + * single primary key is specified in the Criteria + * object and uses it to perform the update. If no primary key is + * specified or the table has multiple primary keys, + * an Exception will be thrown. *

* Use this method for performing an update of the kind: *

- * "WHERE primary_key_id = an int" + * "WHERE primary_key_id = someValue" *

- * To perform an update with non-primary key fields in the WHERE - * clause use doUpdate(criteria, criteria). + * To perform an update on a table with multiple primary keys or + * an update with non-primary key fields in the WHERE + * clause, use doUpdate(ColumnValues, Criteria). + * + * @param updateValues Which columns to update with which values + * for which primary key value, not null. + * + * @return the number of affected rows. * - * @param updateValues A Criteria object containing values used in - * set clause. * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ - public static void doUpdate(Criteria updateValues) throws TorqueException + public static int doUpdate(ColumnValues updateValues) + throws TorqueException { - Connection con = null; + String databaseName; + { + TableMap table = updateValues.getTable(); + if (table == null) + { + log.debug("doUpdate(): no Table set in updateValues, " + + "using default database"); + databaseName = Torque.getDefaultDB(); + } + else + { + databaseName = table.getDatabaseMap().getName(); + } + } + Connection connection = null; try { - con = Transaction.beginOptional( - updateValues.getDbName(), - updateValues.isUseTransaction()); - doUpdate(updateValues, con); - Transaction.commit(con); + connection = Transaction.begin(databaseName); + int result = doUpdate(updateValues, connection); + Transaction.commit(connection); + connection = null; + return result; } - catch (TorqueException e) + finally { - Transaction.safeRollback(con); - throw e; + if (connection != null) + { + Transaction.safeRollback(connection); + } } } /** * Convenience method used to update rows in the DB. Checks if a - * single int primary key is specified in the Criteria - * object and uses it to perform the udpate. If no primary key is - * specified an Exception will be thrown. + * single primary key is specified in the Criteria + * object and uses it to perform the update. If no primary key is + * specified or the table has multiple primary keys, + * an Exception will be thrown. *

* Use this method for performing an update of the kind: *

- * "WHERE primary_key_id = an int" + * "WHERE primary_key_id = someValue" *

- * To perform an update with non-primary key fields in the WHERE - * clause use doUpdate(criteria, criteria). + * To perform an update on a table with multiple primary keys or + * an update with non-primary key fields in the WHERE + * clause, use doUpdate(ColumnValues, Criteria, Connection). + * + * @param updateValues Which columns to update with which values + * for which primary key value, not null. + * @param connection the database connection to use. + * + * @return the number of affected rows. + * + * @param updateValues A map keyed by the column names + * containing the column's values in its values. + * @param connection the database connection to use. * - * @param updateValues A Criteria object containing values used in - * set clause. - * @param con A Connection. * @throws TorqueException Any exceptions caught during processing will be * rethrown wrapped into a TorqueException. */ - public static void doUpdate(Criteria updateValues, Connection con) - throws TorqueException + public static int doUpdate( + ColumnValues updateValues, + Connection connection) + throws TorqueException { - ColumnMap pk = getPrimaryKey(updateValues); + TableMap table = updateValues.getTable(); + ColumnMap pk = table.getPrimaryKey(); Criteria selectCriteria = null; if (pk != null && updateValues.containsKey(pk.getFullyQualifiedName())) @@ -1201,83 +1537,147 @@ public abstract class BasePeer throw new TorqueException("No PK specified for database update"); } - doUpdate(selectCriteria, updateValues, con); + return doUpdate(selectCriteria, updateValues, connection); } /** - * Method used to update rows in the DB. Rows are selected based - * on selectCriteria and updated using values in updateValues. - *

- * Use this method for performing an update of the kind: - *

- * WHERE some_column = some value AND could_have_another_column = - * another value AND so on... + * Executes an update against the database. The rows to be updated + * are selected using criteria and updated using the values + * in updateValues. * - * @param selectCriteria A Criteria object containing values used in where - * clause. - * @param updateValues A Criteria object containing values used in set - * clause. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. + * @param criteria selects which rows of which table should be updated. + * @param updateValues Which columns to update with which values, not null. + * + * @return the number of affected rows. + * + * @throws TorqueException if updating fails. */ - public static void doUpdate(Criteria selectCriteria, Criteria updateValues) - throws TorqueException + public static int doUpdate( + Criteria selectCriteria, + ColumnValues updateValues) + throws TorqueException { - Connection con = null; + String databaseName; + { + TableMap table = updateValues.getTable(); + if (table == null) + { + log.debug("doUpdate(): no Table set in updateValues, " + + "using default database"); + databaseName = Torque.getDefaultDB(); + } + else + { + databaseName = table.getDatabaseMap().getName(); + } + } + Connection connection = null; try { - con = Transaction.beginOptional( - selectCriteria.getDbName(), - updateValues.isUseTransaction()); - doUpdate(selectCriteria, updateValues, con); - Transaction.commit(con); + connection = Transaction.begin(databaseName); + int result = doUpdate(selectCriteria, updateValues, connection); + Transaction.commit(connection); + connection = null; + return result; } - catch (TorqueException e) + finally { - Transaction.safeRollback(con); - throw e; + if (connection != null) + { + Transaction.safeRollback(connection); + } } } /** - * Method used to update rows in the DB. Rows are selected based - * on criteria and updated using values in updateValues. - *

- * Use this method for performing an update of the kind: - *

- * WHERE some_column = some value AND could_have_another_column = - * another value AND so on. + * Executes an update against the database. The rows to be updated + * are selected using criteria and updated using the values + * in updateValues. * - * @param criteria A Criteria object containing values used in where - * clause. - * @param updateValues A Criteria object containing values used in set - * clause. - * @param con A Connection. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. + * @param criteria selects which rows of which table should be updated. + * @param updateValues Which columns to update with which values, not null. + * @param connection the database connection to use, not null. + * + * @return the number of affected rows. + * + * @throws TorqueException if updating fails. */ - public static void doUpdate( - Criteria criteria, - final Criteria updateValues, - Connection con) - throws TorqueException + public static int doUpdate( + Criteria criteria, + ColumnValues updateValues, + Connection connection) + throws TorqueException { - Set tables = SQLBuilder.getTableSet(criteria, null); + Query sql = createQuery(criteria); + sql.setType(Query.Type.UPDATE); + // ignore select columns in criteria + sql.getSelectClause().clear(); + List replacementObjects + = new ArrayList(); + for (Map.Entry updateValue + : updateValues.entrySet()) + { + String columnName = updateValue.getKey(); + if (columnName.lastIndexOf(".") != -1) + { + columnName = columnName.substring( + columnName.lastIndexOf(".") + 1); + } + sql.getSelectClause().add(columnName); + replacementObjects.add(updateValue.getValue()); + } + + PreparedStatement preparedStatement = null; try { - processTables(criteria, tables, con, new ProcessCallback() { - public void process (String table, String dbName, Record rec) - throws Exception - { - // Callback must be called with table name without Schema! - BasePeer.insertOrUpdateRecord(rec, table, dbName, updateValues); - } - }); + preparedStatement = connection.prepareStatement(sql.toString()); + int position = 1; + for (JdbcTypedValue replacementObject : replacementObjects) + { + Object value = replacementObject.getValue(); + if (value != null) + { + preparedStatement.setObject(position, value); + } + else + { + preparedStatement.setNull( + position, + replacementObject.getJdbcType()); + } + position++; + } + long startTime = System.currentTimeMillis(); + log.debug("Executing update " + sql.toString() + + " using parameters " + replacementObjects); + + int affectedRows = preparedStatement.executeUpdate(); + long queryEndTime = System.currentTimeMillis(); + log.trace("update took " + (queryEndTime - startTime) + + " milliseconds"); + + preparedStatement.close(); + preparedStatement = null; + return affectedRows; } - catch (Exception e) + catch (SQLException e) { - throwTorqueException(e); + throw new TorqueException(e); + } + finally + { + if (preparedStatement != null) + { + try + { + preparedStatement.close(); + } + catch (SQLException e) + { + log.warn("error closing prepared statement", e); + } + } } } @@ -1367,23 +1767,6 @@ public abstract class BasePeer } /** - * If the user specified that (s)he only wants to retrieve a - * single record and multiple records are retrieved, this method - * is called to handle the situation. The default behavior is to - * throw an exception, but subclasses can override this method as - * needed. - * - * @param ds The DataSet which contains multiple records. - * @exception TooManyRowsException Couldn't handle multiple records. - */ - protected static void handleMultipleRecords(DataSet ds) - throws TooManyRowsException - { - throw new TooManyRowsException("Criteria expected single Record and " - + "Multiple Records were selected"); - } - - /** * This method returns the MapBuilder specified in the name * parameter. You should pass in the full path to the class, ie: * org.apache.torque.util.db.map.TurbineMapBuilder. The @@ -1401,112 +1784,6 @@ public abstract class BasePeer } /** - * Performs a SQL select using a PreparedStatement. - * Note: this method does not handle null criteria values. - * - * @param criteria - * @param con - * @return a List of Record objects. - * @throws TorqueException Error performing database query. - */ - public static List doPSSelect(Criteria criteria, Connection con) - throws TorqueException - { - List v = null; - - StringBuffer qry = new StringBuffer(); - List params = new ArrayList(criteria.size()); - - createPreparedStatement(criteria, qry, params); - - PreparedStatement statement = null; - try - { - statement = con.prepareStatement(qry.toString()); - - for (int i = 0; i < params.size(); i++) - { - Object param = params.get(i); - if (param instanceof java.sql.Date) - { - statement.setDate(i + 1, (java.sql.Date) param); - } - else if (param instanceof NumberKey) - { - statement.setBigDecimal(i + 1, - ((NumberKey) param).getBigDecimal()); - } - else if (param instanceof Integer) - { - statement.setInt(i + 1, ((Integer) param).intValue()); - } - else - { - statement.setString(i + 1, param.toString()); - } - } - - QueryDataSet qds = null; - try - { - qds = new QueryDataSet(statement.executeQuery()); - v = getSelectResults(qds); - } - finally - { - VillageUtils.close(qds); - } - } - catch (DataSetException e) - { - throwTorqueException(e); - } - catch (SQLException e) - { - throwTorqueException(e); - } - finally - { - if (statement != null) - { - try - { - statement.close(); - } - catch (SQLException e) - { - throw new TorqueException(e); - } - } - } - return v; - } - - /** - * Do a Prepared Statement select according to the given criteria - * - * @param criteria - * @return a List of Record objects. - * @throws TorqueException Any exceptions caught during processing will be - * rethrown wrapped into a TorqueException. - */ - public static List doPSSelect(Criteria criteria) throws TorqueException - { - Connection con = Torque.getConnection(criteria.getDbName()); - List v = null; - - try - { - v = doPSSelect(criteria, con); - } - finally - { - Torque.closeConnection(con); - } - return v; - } - - /** * Create a new PreparedStatement. It builds a string representation * of a query and a list of PreparedStatement parameters. * @@ -1655,116 +1932,46 @@ public abstract class BasePeer } /** - * Process the result of a Table list generation. - * This runs the statements onto the list of tables and - * provides a callback hook to add functionality. - * - * This method should've been in SQLBuilder, but is uses the handleMultipleRecords callback thingie.. - * - * @param crit The criteria - * @param tables A set of Tables to run on - * @param con The SQL Connection to run the statements on - * @param pc A ProcessCallback object + * Checks all columns in the criteria to see whether + * booleanchar and booleanint columns are queried with a boolean. + * If yes, the query values are mapped onto values the database + * does understand, i.e. 0 and 1 for booleanints and N and Y for + * booleanchar columns. * - * @throws Exception An Error occured (should be wrapped into TorqueException) + * @param columnValues The value to be checked for booleanint + * and booleanchar columns. + * @param defaultTableMap the table map to be used if the table name is + * not given in a column. + * @throws TorqueException if the database map for the criteria cannot be + * retrieved. */ - private static void processTables(Criteria crit, Set tables, Connection con, ProcessCallback pc) - throws Exception + public static void correctBooleans( + ColumnValues columnValues) + throws TorqueException { - String dbName = crit.getDbName(); - DB db = Torque.getDB(dbName); - DatabaseMap dbMap = Torque.getDatabaseMap(dbName); - - // create the statements for the tables - for (String table : tables) + TableMap table = columnValues.getTable(); + for (Map.Entry entry : columnValues.entrySet()) { - KeyDef kd = new KeyDef(); - Set whereClause = new HashSet(); - - ColumnMap[] columnMaps = dbMap.getTable(table).getColumns(); - - for (int j = 0; j < columnMaps.length; j++) + String columnName = entry.getKey(); + ColumnMap column = table.getColumn(columnName); + if (column != null) { - ColumnMap colMap = columnMaps[j]; - if (colMap.isPrimaryKey()) + JdbcTypedValue columnValue = entry.getValue(); + if ("BOOLEANINT".equals(column.getTorqueType())) { - kd.addAttrib(colMap.getColumnName()); + columnValue.setValue( + Boolean.TRUE.equals(columnValue.getValue()) + ? new Integer(1) + : new Integer(0)); } - - String key = new StringBuffer(colMap.getTableName()) - .append('.') - .append(colMap.getColumnName()) - .toString(); - - if (crit.containsKey(key)) + else if ("BOOLEANCHAR".equals(column.getTorqueType())) { - if (crit - .getComparison(key) - .equals(Criteria.CUSTOM)) - { - whereClause.add(crit.getString(key)); - } - else - { - whereClause.add( - SqlExpression.build( - colMap.getColumnName(), - crit.getValue(key), - crit.getComparison(key), - crit.isIgnoreCase(), - db)); - } - } - } - - // Execute the statement for each table - TableDataSet tds = null; - try - { - String tableName = SQLBuilder.getFullTableName(table, dbName); - - // Get affected records. - tds = new TableDataSet(con, tableName, kd); - String sqlSnippet = StringUtils.join(whereClause.iterator(), " AND "); - - if (log.isDebugEnabled()) - { - log.debug("BasePeer: whereClause=" + sqlSnippet); - } - - tds.where(sqlSnippet); - tds.fetchRecords(); - - if (tds.size() > 1 && crit.isSingleRecord()) - { - handleMultipleRecords(tds); - } - - for (int j = 0; j < tds.size(); j++) - { - Record rec = tds.getRecord(j); - - if (pc != null) - { - // Table name _without_ schema! - pc.process(table, dbName, rec); - } - } - } - finally - { - VillageUtils.close(tds); + columnValue.setValue( + Boolean.TRUE.equals(columnValue.getValue()) + ? "Y" + : "N"); + } } } } - - /** - * Inner Interface that defines the Callback method for - * the Record Processing - */ - protected interface ProcessCallback - { - void process (String table, String dbName, Record rec) - throws Exception; - } } --------------------------------------------------------------------- To unsubscribe, e-mail: torque-dev-unsubscribe@db.apache.org For additional commands, e-mail: torque-dev-help@db.apache.org