View Javadoc

1   package org.apache.torque.util;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.Serializable;
23  import java.math.BigDecimal;
24  import java.sql.Connection;
25  import java.sql.PreparedStatement;
26  import java.sql.ResultSet;
27  import java.sql.SQLException;
28  import java.sql.Statement;
29  import java.sql.Types;
30  import java.util.ArrayList;
31  import java.util.List;
32  import java.util.Map;
33  
34  import org.apache.commons.lang.StringUtils;
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.torque.Column;
38  import org.apache.torque.Database;
39  import org.apache.torque.TooManyRowsException;
40  import org.apache.torque.Torque;
41  import org.apache.torque.TorqueException;
42  import org.apache.torque.adapter.Adapter;
43  import org.apache.torque.adapter.IDMethod;
44  import org.apache.torque.criteria.FromElement;
45  import org.apache.torque.map.ColumnMap;
46  import org.apache.torque.map.MapHelper;
47  import org.apache.torque.map.TableMap;
48  import org.apache.torque.oid.IdGenerator;
49  import org.apache.torque.om.NumberKey;
50  import org.apache.torque.om.ObjectKey;
51  import org.apache.torque.om.SimpleKey;
52  import org.apache.torque.om.StringKey;
53  import org.apache.torque.om.mapper.RecordMapper;
54  import org.apache.torque.sql.Query;
55  import org.apache.torque.sql.SqlBuilder;
56  
57  /**
58   * This is the base class for all Peer classes in the system.  Peer
59   * classes are responsible for isolating all of the database access
60   * for a specific business object.  They execute all of the SQL
61   * against the database.  Over time this class has grown to include
62   * utility methods which ease execution of cross-database queries and
63   * the implementation of concrete Peers.
64   *
65   * @param <T> The data object class for this Peer.
66   *
67   * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
68   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
69   * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
70   * @author <a href="mailto:stephenh@chase3000.com">Stephen Haberman</a>
71   * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
72   * @author <a href="mailto:vido@ldh.org">Augustin Vidovic</a>
73   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
74   * @version $Id: BasePeerImpl.java 1388656 2012-09-21 19:59:16Z tfischer $
75   */
76  public class BasePeerImpl<T> implements Serializable
77  {
78      /**
79       * Serial Version
80       */
81      private static final long serialVersionUID = -7702123730779032381L;
82  
83      /** the log */
84      private static final Log log = LogFactory.getLog(BasePeerImpl.class);
85  
86      /** An injected instance of a record mapper to map JDBC result sets to objects */
87      private RecordMapper<T> recordMapper = null;
88  
89      /** An injected instance of a table map */
90      private TableMap tableMap = null;
91  
92      /** An injected instance of the database name */
93      private String databaseName = null;
94  
95      /**
96       * Default constructor
97       */
98      public BasePeerImpl()
99      {
100         super();
101     }
102 
103     /**
104      * Constructor providing the objects to be injected as parameters.
105      *
106      * @param recordMapper a record mapper to map JDBC result sets to objects
107      * @param tableMap the default table map
108      * @param databaseName the name of the database
109      */
110     public BasePeerImpl(RecordMapper<T> recordMapper, TableMap tableMap, String databaseName)
111     {
112         this();
113         setRecordMapper(recordMapper);
114         setTableMap(tableMap);
115         setDatabaseName(databaseName);
116     }
117 
118     /**
119      * Set the record mapper for this instance.
120      *
121      * @param recordMapper the recordMapper to set
122      */
123     public void setRecordMapper(RecordMapper<T> recordMapper)
124     {
125         this.recordMapper = recordMapper;
126     }
127 
128     /**
129      * Get the record mapper for this instance.
130      *
131      * @return the recordMapper
132      */
133     public RecordMapper<T> getRecordMapper() throws TorqueException
134     {
135         if (recordMapper == null)
136         {
137             throw new TorqueException("No record mapper injected");
138         }
139 
140         return recordMapper;
141     }
142 
143     /**
144      * Set the default table map for this instance.
145      *
146      * @param tableMap the tableMap to set
147      */
148     public void setTableMap(TableMap tableMap)
149     {
150         this.tableMap = tableMap;
151     }
152 
153     /**
154      * Get the default table map for this instance.
155      *
156      * @return the tableMap
157      */
158     public TableMap getTableMap() throws TorqueException
159     {
160         if (tableMap == null)
161         {
162             throw new TorqueException("No table map injected");
163         }
164 
165         return tableMap;
166     }
167 
168     /**
169      * Set the database name for this instance.
170      *
171      * @param databaseName the databaseName to set
172      */
173     public void setDatabaseName(String databaseName)
174     {
175         this.databaseName = databaseName;
176     }
177 
178     /**
179      * Get the database name for this instance.
180      *
181      * @return the databaseName
182      */
183     public String getDatabaseName() throws TorqueException
184     {
185         if (databaseName == null)
186         {
187             throw new TorqueException("No database name injected");
188         }
189 
190         return databaseName;
191     }
192 
193     /**
194      * Get the list of objects from a ResultSet.
195      * Please not that the ResultSet MUST return columns in the right order.
196      * You can use getFieldNames() in BaseObject to get the correct sequence.
197      *
198      * @param resultSet the ResultSet to extract the objects from.
199      * @param the database adapter of the database for the result set,
200      *        or null if the adapter is unknown.
201      *
202      * @return the list of objects read from the ResultSet.
203      *
204      * @throws TorqueException If the mapping fails.
205      */
206     public List<T> resultSet2Objects(
207             java.sql.ResultSet resultSet,
208             Adapter adapter)
209         throws TorqueException
210     {
211         try
212         {
213             List<T> result = new ArrayList<T>();
214             RecordMapper<T> mapper = getRecordMapper();
215             while (resultSet.next())
216             {
217                 result.add(mapper.processRow(resultSet, 0));
218             }
219             return result;
220         }
221         catch (SQLException e)
222         {
223             if (adapter != null)
224             {
225                 throw adapter.toTorqueException(e);
226             }
227             throw new TorqueException(e);
228         }
229     }
230 
231     /**
232      * Convenience method to create a String array of criteria keys.
233      *
234      * @param tableName Name of table.
235      * @param columnNames A String[].
236      * @return A String[].
237      *
238      * @deprecated This method is not used any more and will be removed in a
239      *             future version of Torque.
240      */
241     @Deprecated
242     public String[] initCriteriaKeys(
243         String tableName,
244         String[] columnNames)
245     {
246         String[] keys = new String[columnNames.length];
247         for (int i = 0; i < columnNames.length; i++)
248         {
249             keys[i] = tableName + "." + columnNames[i].toUpperCase();
250         }
251         return keys;
252     }
253 
254     /**
255      * Convenience method that uses straight JDBC to delete multiple
256      * rows.
257      *
258      * @param con A Connection.
259      * @param table The table to delete records from.
260      * @param column The column in the where clause.
261      * @param value The value of the column.
262      *
263      * @return the number of deleted rows.
264      *
265      * @throws TorqueException Any exceptions caught during processing will be
266      *         rethrown wrapped into a TorqueException.
267      *
268      * @deprecated The value is not SQL escaped.
269      *             Better use doDelete(Criteria, String, Connection)
270      *             for automatic escaping and more flexibility.
271      *             This method will be removed in a future version of Torque.
272      */
273     @Deprecated
274     public int deleteAll(
275                 Connection con,
276                 String table,
277                 String column,
278                 int value)
279             throws TorqueException
280     {
281         Statement statement = null;
282         try
283         {
284             statement = con.createStatement();
285 
286             StringBuffer query = new StringBuffer();
287             query.append("DELETE FROM ")
288                 .append(table)
289                 .append(" WHERE ")
290                 .append(column)
291                 .append(" = ")
292                 .append(value);
293 
294             return statement.executeUpdate(query.toString());
295         }
296         catch (SQLException e)
297         {
298             throw new TorqueException(e);
299         }
300         finally
301         {
302             if (statement != null)
303             {
304                 try
305                 {
306                     statement.close();
307                 }
308                 catch (SQLException e)
309                 {
310                     throw new TorqueException(e);
311                 }
312             }
313         }
314     }
315 
316     /**
317      * Convenience method that uses straight JDBC to delete multiple
318      * rows. This method attempts to get the default database from
319      * the pool.
320      *
321      * @param table The table to delete records from.
322      * @param column The column in the where clause.
323      * @param value The value of the column.
324      *
325      * @return the number of deleted rows.
326      *
327      * @throws TorqueException Any exceptions caught during processing will be
328      *         rethrown wrapped into a TorqueException.
329      *
330      * @deprecated The value is not SQL escaped.
331      *             Better use doDelete(Criteria, String)
332      *             for automatic escaping and more flexibility.
333      *             This method will be removed in a future version of Torque.
334      */
335     @Deprecated
336     public int deleteAll(String table, String column, int value)
337         throws TorqueException
338     {
339         Connection con = null;
340         try
341         {
342             con = Transaction.begin(Torque.getDefaultDB());
343             int result = deleteAll(con, table, column, value);
344             Transaction.commit(con);
345             con = null;
346             return result;
347         }
348         finally
349         {
350             if (con != null)
351             {
352                 Transaction.safeRollback(con);
353             }
354         }
355     }
356 
357     /**
358      * Deletes rows from a database table.
359      *
360      * @param criteria defines the rows to be deleted, not null.
361      *
362      * @return the number of deleted rows.
363      *
364      * @throws TorqueException Any exceptions caught during processing will be
365      *         rethrown wrapped into a TorqueException.
366      */
367      public int doDelete(org.apache.torque.criteria.Criteria criteria)
368              throws TorqueException
369      {
370         Connection connection = null;
371         try
372         {
373             setDbName(criteria);
374             connection = Transaction.begin(criteria.getDbName());
375             int deletedRows = doDelete(criteria, connection);
376             Transaction.commit(connection);
377             connection = null;
378             return deletedRows;
379         }
380         finally
381         {
382             if (connection != null)
383             {
384                 Transaction.safeRollback(connection);
385             }
386         }
387      }
388 
389     /**
390      * Deletes rows from a table.  This method is to be used
391      * during a transaction, otherwise use the doDelete(Criteria) method.
392       *
393      * @param criteria defines the rows to be deleted, not null.
394      * @param connection the connection to use, not null.
395      *
396      * @return the number of deleted rows.
397      *
398      * @throws TorqueException Any exceptions caught during processing will be
399      *         rethrown wrapped into a TorqueException.
400      */
401      public int doDelete(
402              org.apache.torque.criteria.Criteria criteria,
403              Connection connection)
404         throws TorqueException
405      {
406         correctBooleans(criteria);
407         setDbName(criteria);
408 
409         Query query = SqlBuilder.buildQuery(criteria);
410         query.setType(Query.Type.DELETE);
411 
412         String fullTableName;
413         if (tableMap == null)
414         {
415             fullTableName = SqlBuilder.guessFullTableFromCriteria(criteria);
416         }
417         else
418         {
419             fullTableName = SqlBuilder.getFullTableName(
420                     tableMap.getName(),
421                     criteria.getDbName());
422         }
423         boolean ownTableAdded = false;
424         for (FromElement fromElement : query.getFromClause())
425         {
426             // Table names are case insensitive in known databases
427             // so use case-insensitive compare
428             if (fullTableName.equalsIgnoreCase(fromElement.getFromExpression()))
429             {
430                 ownTableAdded = true;
431                 break;
432             }
433         }
434         if (!ownTableAdded)
435         {
436             query.getFromClause().add(new FromElement(fullTableName));
437         }
438         String sql = query.toString();
439 
440         PreparedStatement preparedStatement = null;
441         try
442         {
443             preparedStatement = connection.prepareStatement(sql);
444             List<Object> replacements = setPreparedStatementReplacements(
445                     preparedStatement,
446                     query.getPreparedStatementReplacements(),
447                     0);
448             long startTime = System.currentTimeMillis();
449             log.debug("Executing delete " + sql
450                     + ", parameters = "
451                     + replacements);
452 
453             int affectedRows = preparedStatement.executeUpdate();
454             long queryEndTime = System.currentTimeMillis();
455             log.trace("delete took " + (queryEndTime - startTime)
456                     + " milliseconds");
457 
458             preparedStatement.close();
459             preparedStatement = null;
460             return affectedRows;
461         }
462         catch (SQLException e)
463         {
464             final String dbName = criteria.getDbName();
465             final Adapter adapter = Torque.getAdapter(dbName);
466             if (adapter != null)
467             {
468                 throw adapter.toTorqueException(e);
469             }
470             throw new TorqueException(e);
471         }
472         finally
473         {
474             if (preparedStatement != null)
475             {
476                 try
477                 {
478                     preparedStatement.close();
479                 }
480                 catch (SQLException e)
481                 {
482                     log.warn("error closing prepared statement", e);
483                 }
484             }
485         }
486      }
487 
488     /**
489      * Method to perform deletes based on conditions in a Criteria.
490      *
491      * @param criteria The criteria to use.
492      *
493      * @return the number of deleted rows.
494      *
495      * @throws TorqueException Any exceptions caught during processing will be
496      *         rethrown wrapped into a TorqueException.
497      *
498      * @deprecated This method causes unexpected results when joins are used.
499      *             Please use doDelete(
500      *                 org.apache.torque.criteria.Criteria, TableMap).
501      *             This method will be removed in a future version of Torque.
502      */
503     @Deprecated
504     protected int doDelete(Criteria criteria) throws TorqueException
505     {
506         Connection con = null;
507         try
508         {
509             con = Transaction.begin(criteria.getDbName());
510             int result = doDelete(criteria, con);
511             Transaction.commit(con);
512             con = null;
513             return result;
514         }
515         finally
516         {
517             if (con != null)
518             {
519                 Transaction.safeRollback(con);
520             }
521         }
522     }
523 
524     /**
525      * Method to perform deletes based on conditions a Criteria.
526      *
527      * @param criteria The criteria to use.
528      * @param con the Connection to be used for deleting.
529      *
530      * @return the number of deleted rows.
531      *
532      * @throws TorqueException Any exceptions caught during processing will be
533      *         rethrown wrapped into a TorqueException.
534      *
535      * @deprecated This method causes unexpected results when joins are used.
536      *             Please use doDelete(
537      *                 org.apache.torque.criteria.Criteria, TableMap, Connection).
538      *             This method will be removed in a future version of Torque.
539      */
540     @Deprecated
541     protected int doDelete(Criteria criteria, Connection con)
542         throws TorqueException
543     {
544         if (criteria.values().isEmpty())
545         {
546             throw new TorqueException("No conditions found in Criteria");
547         }
548         Criteria.Criterion criterion
549                 = criteria.values().iterator().next();
550 
551         TableMap tableMapFromCriteria = MapHelper.getTableMap(
552                 criterion.getColumn(), criteria, null);
553         if (tableMapFromCriteria == null)
554         {
555             throw new TorqueException("Unqualified column name in criteria"
556                     + " or table name not found in database map");
557         }
558 
559         Query query = SqlBuilder.buildQuery(criteria);
560         query.setType(Query.Type.DELETE);
561 
562         String fullTableName = null;
563         if (tableMap != null)
564         {
565             fullTableName = SqlBuilder.getFullTableName(
566                     tableMap.getName(),
567                     criteria.getDbName());
568         }
569         else
570         {
571             Column column = criteria.values().iterator().next().getColumn();
572             fullTableName = SqlBuilder.getFullTableName(
573                     column.getFullTableName(),
574                     criteria.getDbName());
575         }
576 
577         boolean ownTableAdded = false;
578         for (FromElement fromElement : query.getFromClause())
579         {
580             // Table names are case insensitive in known databases
581             // so use case-insensitive compare
582             if (fullTableName.equalsIgnoreCase(fromElement.getFromExpression()))
583             {
584                 ownTableAdded = true;
585                 break;
586             }
587         }
588         if (!ownTableAdded)
589         {
590             query.getFromClause().add(new FromElement(fullTableName));
591         }
592         String sql = query.toString();
593 
594         PreparedStatement preparedStatement = null;
595         try
596         {
597             preparedStatement = con.prepareStatement(sql);
598             List<Object> replacements = setPreparedStatementReplacements(
599                     preparedStatement,
600                     query.getPreparedStatementReplacements(),
601                     0);
602             long startTime = System.currentTimeMillis();
603             log.debug("Executing delete " + sql
604                     + ", parameters = "
605                     + replacements);
606 
607             int affectedRows = preparedStatement.executeUpdate();
608             long queryEndTime = System.currentTimeMillis();
609             log.trace("delete took " + (queryEndTime - startTime)
610                     + " milliseconds");
611 
612             preparedStatement.close();
613             preparedStatement = null;
614             return affectedRows;
615         }
616         catch (SQLException e)
617         {
618             final String dbName = criteria.getDbName();
619             final Adapter adapter = Torque.getAdapter(dbName);
620             if (adapter != null)
621             {
622                 throw adapter.toTorqueException(e);
623             }
624             throw new TorqueException(e);
625         }
626         finally
627         {
628             if (preparedStatement != null)
629             {
630                 try
631                 {
632                     preparedStatement.close();
633                 }
634                 catch (SQLException e)
635                 {
636                     log.warn("error closing prepared statement", e);
637                 }
638             }
639         }
640     }
641 
642     /**
643      * Inserts a record into a database table.
644      * <p>
645      * If the primary key is included in Criteria, then that value will
646      * be used to insert the row.
647      * <p>
648      * Otherwise, if the primary key can be generated automatically,
649      * the generated key will be used for the insert and will be returned.
650      * <p>
651      * If no value is given for the primary key is defined and it cannot
652      * be generated automatically or the table has no primary key,
653      * the values will be inserted as specified and null will be returned.
654      *
655      * @param insertValues Contains the values to insert, not null.
656      *
657      * @return the primary key of the inserted row (if the table
658      *         has a primary key) or null (if the table does not have
659      *         a primary key).
660      *
661      * @throws TorqueException if a database error occurs.
662      */
663     public ObjectKey doInsert(ColumnValues insertValues)
664           throws TorqueException
665     {
666         String databaseNameFromInsertValues = insertValues.getDbName();
667         if (databaseNameFromInsertValues == null)
668         {
669             databaseNameFromInsertValues = getDatabaseName();
670         }
671         Connection connection = null;
672         try
673         {
674             connection = Transaction.begin(databaseNameFromInsertValues);
675             ObjectKey id = doInsert(insertValues, connection);
676             Transaction.commit(connection);
677             connection = null;
678             return id;
679         }
680         finally
681         {
682             if (connection != null)
683             {
684                 Transaction.safeRollback(connection);
685             }
686         }
687     }
688 
689     /**
690      * Inserts a record into a database table.
691      * <p>
692      * If the primary key is included in Criteria, then that value will
693      * be used to insert the row.
694      * <p>
695      * Otherwise, if the primary key can be generated automatically,
696      * the generated key will be used for the insert and will be returned.
697      * <p>
698      * If no value is given for the primary key is defined and it cannot
699      * be generated automatically or the table has no primary key,
700      * the values will be inserted as specified and null will be returned.
701      *
702      * @param insertValues Contains the values to insert, not null.
703      * @param connection the connection to use for the insert, not null.
704      *
705      * @return the primary key of the inserted row (if the table
706      *         has a primary key) or null (if the table does not have
707      *         a primary key).
708      *
709      * @throws TorqueException if a database error occurs.
710      */
711     public ObjectKey doInsert(
712                 ColumnValues insertValues,
713                 Connection connection)
714             throws TorqueException
715     {
716         if (insertValues == null)
717         {
718             throw new TorqueException("insertValues is null");
719         }
720         if (connection == null)
721         {
722             throw new TorqueException("connection is null");
723         }
724         String databaseNameFromInsertValues = insertValues.getDbName();
725         if (databaseNameFromInsertValues == null)
726         {
727             databaseNameFromInsertValues = getDatabaseName();
728         }
729         Database database = Torque.getDatabase(databaseNameFromInsertValues);
730         Object keyInfo = getIdMethodInfo();
731         IdGenerator keyGen = database.getIdGenerator(
732                 getTableMap().getPrimaryKeyMethod());
733 
734         SimpleKey id = null;
735         // can currently generate only single column pks, therefore a single
736         // columnMap is ok
737         ColumnMap primaryKey = null;
738         if (keyGen != null)
739         {
740             // fail on multiple pks
741             primaryKey = getTableMap().getPrimaryKey();
742 
743             // primaryKey will be null if there is no primary key
744             // defined for the table we're inserting into.
745             if (keyGen.isPriorToInsert() && primaryKey != null
746                     && !insertValues.containsKey(
747                             primaryKey))
748             {
749                 id = getId(primaryKey, keyGen, connection, keyInfo);
750                 insertValues.put(
751                         primaryKey,
752                         new JdbcTypedValue(id.getValue(), id.getJdbcType()));
753             }
754         }
755 
756         List<String> columnNames = new ArrayList<String>();
757         List<JdbcTypedValue> replacementObjects
758                 = new ArrayList<JdbcTypedValue>();
759         for (Map.Entry<Column, JdbcTypedValue> columnValue
760                 : insertValues.entrySet())
761         {
762             Column column = columnValue.getKey();
763             columnNames.add(column.getColumnName());
764             JdbcTypedValue value = columnValue.getValue();
765             replacementObjects.add(value);
766         }
767 
768         String fullTableName = SqlBuilder.getFullTableName(
769                 getTableMap().getName(),
770                 databaseNameFromInsertValues);
771         StringBuilder query = new StringBuilder("INSERT INTO ")
772             .append(fullTableName)
773             .append("(")
774             .append(StringUtils.join(columnNames, ","))
775             .append(") VALUES (");
776         for (int i = 0; i < columnNames.size(); ++i)
777         {
778             if (i != 0)
779             {
780                 query.append(",");
781             }
782             query.append("?");
783         }
784         query.append(")");
785 
786         PreparedStatement preparedStatement = null;
787         try
788         {
789             preparedStatement = connection.prepareStatement(query.toString());
790             int position = 1;
791             for (JdbcTypedValue replacementObject : replacementObjects)
792             {
793                 Object value = replacementObject.getValue();
794                 if (value != null)
795                 {
796                     if (replacementObject.getJdbcType() != Types.BLOB
797                             && replacementObject.getJdbcType() != Types.CLOB)
798                     {
799                         preparedStatement.setObject(
800                                 position,
801                                 value,
802                                 replacementObject.getJdbcType());
803                     }
804                     else
805                     {
806                         preparedStatement.setObject(
807                                 position,
808                                 value);
809                     }
810                 }
811                 else
812                 {
813                     preparedStatement.setNull(
814                             position,
815                             replacementObject.getJdbcType());
816                 }
817                 position++;
818             }
819             long startTime = System.currentTimeMillis();
820             log.debug("Executing insert " + query.toString()
821                     + " using parameters " + replacementObjects);
822 
823             preparedStatement.executeUpdate();
824             long queryEndTime = System.currentTimeMillis();
825             log.trace("insert took " + (queryEndTime - startTime)
826                     + " milliseconds");
827 
828             preparedStatement.close();
829             preparedStatement = null;
830         }
831         catch (SQLException e)
832         {
833             final String dbName = insertValues.getDbName();
834             final Adapter adapter = Torque.getAdapter(dbName);
835             if (adapter != null)
836             {
837                 throw adapter.toTorqueException(e);
838             }
839             throw new TorqueException(e);
840         }
841         finally
842         {
843             if (preparedStatement != null)
844             {
845                 try
846                 {
847                     preparedStatement.close();
848                 }
849                 catch (SQLException e)
850                 {
851                     log.warn("error closing prepared statement", e);
852                 }
853             }
854         }
855 
856         // If the primary key column is auto-incremented, get the id
857         // now.
858         if (keyGen != null && keyGen.isPostInsert()
859                 && primaryKey != null
860                 && !insertValues.containsKey(
861                         primaryKey))
862         {
863             id = getId(primaryKey, keyGen, connection, keyInfo);
864         }
865 
866         return id;
867     }
868 
869     /**
870      * Returns the idMethodInfo for the table for this Peer class.
871      *
872      * @return the idMethodInfo, not null.
873      *
874      * @throws TorqueException if the database adapter for the table's database
875      *         needs to be accessed but is not configured.
876      */
877     private Object getIdMethodInfo()
878             throws TorqueException
879     {
880         IDMethod idMethod = tableMap.getPrimaryKeyMethod();
881         if (IDMethod.NATIVE == idMethod)
882         {
883             Adapter adapter = Torque.getAdapter(getDatabaseName());
884             if (adapter == null)
885             {
886                throw new TorqueException(
887                    "missing adapter configuration for database "
888                        + getDatabaseName()
889                        + "check the Torque configuration");
890             }
891             idMethod = adapter.getIDMethodType();
892         }
893         Object keyInfo = tableMap.getPrimaryKeyMethodInfo(idMethod);
894         return keyInfo;
895     }
896 
897     /**
898      * Create an Id for insertion in the Criteria
899      *
900      * @param pk ColumnMap for the Primary key
901      * @param keyGen The Id Generator object
902      * @param con The SQL Connection to run the id generation under
903      * @param keyInfo KeyInfo Parameter from the Table map
904      *
905      * @return A simple Key representing the new Id value
906      * @throws TorqueException Possible errors get wrapped in here.
907      */
908     private SimpleKey getId(
909                 ColumnMap pk,
910                 IdGenerator keyGen,
911                 Connection con,
912                 Object keyInfo)
913             throws TorqueException
914     {
915         SimpleKey id = null;
916 
917         if (pk != null && keyGen != null)
918         {
919             if (pk.getType() instanceof Number)
920             {
921                 id = new NumberKey(
922                         keyGen.getIdAsBigDecimal(con, keyInfo));
923             }
924             else
925             {
926                 id = new StringKey(keyGen.getIdAsString(con, keyInfo));
927             }
928         }
929 
930         return id;
931     }
932 
933     /**
934      * Add all the columns needed to create a new object.
935      *
936      * @param criteria the Criteria to which the select columns should
937      *        be added.
938      */
939     public void addSelectColumns(org.apache.torque.criteria.Criteria criteria)
940     {
941         ColumnMap[] columns = this.tableMap.getColumns();
942 
943         for (ColumnMap c : columns)
944         {
945             criteria.addSelectColumn(c);
946         }
947     }
948 
949     /**
950      * Add all the columns needed to create a new object.
951      *
952      * @param criteria the Criteria to which the select columns should
953      *        be added.
954      *
955      * @deprecated Please use addSelectColumns(
956      *                 org.apache.torque.criteria.Criteria).
957      *             This method will be removed in a future version of Torque.
958      */
959     @Deprecated
960     public void addSelectColumns(Criteria criteria)
961     {
962         ColumnMap[] columns = this.tableMap.getColumns();
963 
964         for (ColumnMap c : columns)
965         {
966             criteria.addSelectColumn(c);
967         }
968     }
969 
970     /**
971      * Selects objects from a database.
972      *
973      * @param criteria object used to create the SELECT statement.
974      *
975      * @return the list of selected objects, not null.
976      *
977      * @throws TorqueException Any exceptions caught during processing will be
978      *         rethrown wrapped into a TorqueException.
979      *
980      * @deprecated Please use doSelect(org.apache.torque.criteria.Criteria).
981      *             This method will be removed in a future version of Torque.
982      */
983     @Deprecated
984     public List<T> doSelect(Criteria criteria)
985             throws TorqueException
986     {
987         if (criteria.getSelectColumns().size() == 0)
988         {
989             addSelectColumns(criteria);
990         }
991         setDbName(criteria);
992 
993         return doSelect(
994             criteria,
995             getRecordMapper());
996     }
997 
998     /**
999      * Selects objects from a database.
1000      *
1001      * @param criteria object used to create the SELECT statement.
1002      *
1003      * @return the list of selected objects, not null.
1004      *
1005      * @throws TorqueException Any exceptions caught during processing will be
1006      *         rethrown wrapped into a TorqueException.
1007      */
1008     public List<T> doSelect(org.apache.torque.criteria.Criteria criteria)
1009             throws TorqueException
1010     {
1011         if (criteria.getSelectColumns().size() == 0)
1012         {
1013             addSelectColumns(criteria);
1014         }
1015         setDbName(criteria);
1016 
1017         return doSelect(
1018             criteria,
1019             getRecordMapper());
1020     }
1021 
1022     /**
1023      * Selects objects from a database
1024      * within a transaction.
1025      *
1026      * @param criteria object used to create the SELECT statement.
1027      * @param connection the connection to use, not null.
1028      *
1029      * @return the list of selected objects, not null.
1030      *
1031      * @throws TorqueException Any exceptions caught during processing will be
1032      *         rethrown wrapped into a TorqueException.
1033      *
1034      * @deprecated Please use doSelect(org.apache.torque.criteria.Criteria,
1035      *                 Connection).
1036      *             This method will be removed in a future version of Torque.
1037      */
1038     @Deprecated
1039     public List<T> doSelect(
1040                 Criteria criteria,
1041                 Connection connection)
1042             throws TorqueException
1043     {
1044         if (criteria.getSelectColumns().size() == 0)
1045         {
1046             addSelectColumns(criteria);
1047         }
1048         setDbName(criteria);
1049 
1050         return doSelect(
1051                 criteria,
1052                 getRecordMapper(),
1053                 connection);
1054     }
1055 
1056     /**
1057      * Selects objects from a database
1058      * within a transaction.
1059      *
1060      * @param criteria object used to create the SELECT statement.
1061      * @param connection the connection to use, not null.
1062      *
1063      * @return the list of selected objects, not null.
1064      *
1065      * @throws TorqueException Any exceptions caught during processing will be
1066      *         rethrown wrapped into a TorqueException.
1067      */
1068     public List<T> doSelect(
1069                 org.apache.torque.criteria.Criteria criteria,
1070                 Connection connection)
1071             throws TorqueException
1072     {
1073         if (criteria.getSelectColumns().size() == 0)
1074         {
1075             addSelectColumns(criteria);
1076         }
1077         setDbName(criteria);
1078 
1079         return doSelect(
1080                 criteria,
1081                 getRecordMapper(),
1082                 connection);
1083     }
1084 
1085     /**
1086      * Selects at most one object from a database.
1087      *
1088      * @param criteria object used to create the SELECT statement.
1089      *
1090      * @return the selected Object, or null if no object was selected.
1091      *
1092      * @throws TorqueException If more than one record is selected or if
1093      *         an error occurs when processing the query.
1094      */
1095     public T doSelectSingleRecord(org.apache.torque.criteria.Criteria criteria)
1096             throws TorqueException
1097     {
1098         List<T> recordList = doSelect(criteria);
1099         T record = null;
1100         if (recordList.size() > 1)
1101         {
1102             throw new TooManyRowsException("Criteria " + criteria
1103                 + " matched more than one record");
1104         }
1105         if (!recordList.isEmpty())
1106         {
1107             record = recordList.get(0);
1108         }
1109         return record;
1110     }
1111 
1112     /**
1113      * Selects at most one object from a database
1114      * within a transaction.
1115      *
1116      * @param criteria object used to create the SELECT statement.
1117      * @param connection the connection holding the transaction, not null.
1118      *
1119      * @return the selected Object, or null if no object was selected.
1120      *
1121      * @throws TorqueException If more than one record is selected or if
1122      *         an error occurs when processing the query.
1123      */
1124     public T doSelectSingleRecord(
1125                 org.apache.torque.criteria.Criteria criteria,
1126                 Connection connection)
1127             throws TorqueException
1128     {
1129         List<T> recordList = doSelect(criteria, connection);
1130         T record = null;
1131         if (recordList.size() > 1)
1132         {
1133             throw new TooManyRowsException("Criteria " + criteria
1134                 + " matched more than one record");
1135         }
1136         if (!recordList.isEmpty())
1137         {
1138             record = recordList.get(0);
1139         }
1140         return record;
1141     }
1142 
1143     /**
1144      * Selects rows from a database an maps them to objects.
1145      *
1146      * @param criteria A Criteria specifying the records to select, not null.
1147      * @param mapper The mapper creating the objects from the resultSet,
1148      *        not null.
1149      *
1150      * @return The results of the query, not null.
1151      *
1152      * @throws TorqueException if querying the database fails.
1153      *
1154      * @deprecated Please use doSelect(org.apache.torque.criteria.Criteria,
1155      *                 RecordMapper).
1156      *             This method will be removed in a future version of Torque.
1157      */
1158     @Deprecated
1159     public <TT> List<TT> doSelect(
1160                 Criteria criteria,
1161                 RecordMapper<TT> mapper)
1162             throws TorqueException
1163     {
1164         Connection connection = null;
1165         try
1166         {
1167             connection = Transaction.begin(criteria.getDbName());
1168 
1169             List<TT> result = doSelect(
1170                     criteria,
1171                     mapper,
1172                     connection);
1173 
1174             Transaction.commit(connection);
1175             connection = null;
1176             return result;
1177         }
1178         finally
1179         {
1180             if (connection != null)
1181             {
1182                 Transaction.safeRollback(connection);
1183             }
1184         }
1185     }
1186 
1187     /**
1188      * Selects rows from a database an maps them to objects.
1189      *
1190      * @param criteria A Criteria specifying the records to select, not null.
1191      * @param mapper The mapper creating the objects from the resultSet,
1192      *        not null.
1193      *
1194      * @return The results of the query, not null.
1195      *
1196      * @throws TorqueException if querying the database fails.
1197      */
1198     public <TT> List<TT> doSelect(
1199                 org.apache.torque.criteria.Criteria criteria,
1200                 RecordMapper<TT> mapper)
1201             throws TorqueException
1202     {
1203         Connection connection = null;
1204         try
1205         {
1206             connection = Transaction.begin(criteria.getDbName());
1207 
1208             List<TT> result = doSelect(
1209                     criteria,
1210                     mapper,
1211                     connection);
1212 
1213             Transaction.commit(connection);
1214             connection = null;
1215             return result;
1216         }
1217         finally
1218         {
1219             if (connection != null)
1220             {
1221                 Transaction.safeRollback(connection);
1222             }
1223         }
1224     }
1225 
1226     /**
1227      * Selects rows from a database an maps them to objects.
1228      *
1229      * @param query the sql query to execute, not null.
1230      *
1231      * @return The results of the query, not null.
1232      *
1233      * @throws TorqueException if querying the database fails.
1234      */
1235     public List<T> doSelect(String query)
1236             throws TorqueException
1237     {
1238         return doSelect(
1239                 query,
1240                 getRecordMapper(),
1241                 getDatabaseName());
1242     }
1243 
1244     /**
1245      * Selects rows from a database an maps them to objects.
1246      *
1247      * @param query the SQL Query to execute, not null.
1248      * @param dbName The name of the database to select from,
1249      *        or null for the default DB.
1250      * @param connection the database connection, not null.
1251      *
1252      * @return The results of the query, not null.
1253      *
1254      * @throws TorqueException if querying the database fails.
1255      */
1256     public List<T> doSelect(
1257                 String query,
1258                 String dbName,
1259                 Connection connection)
1260             throws TorqueException
1261     {
1262         return doSelect(
1263                 query,
1264                 getRecordMapper(),
1265                 dbName,
1266                 connection);
1267     }
1268 
1269     /**
1270      * Selects rows from a database an maps them to objects.
1271      *
1272      * @param query the sql query to execute, not null.
1273      * @param mapper The mapper creating the objects from the resultSet,
1274      *        not null.
1275      * @param dbName The name of the database to create the connection for,
1276      *        or null for the default DB.
1277      *
1278      * @return The results of the query, not null.
1279      *
1280      * @throws TorqueException if querying the database fails.
1281      */
1282     public <TT> List<TT> doSelect(
1283                 String query,
1284                 RecordMapper<TT> mapper,
1285                 String dbName)
1286             throws TorqueException
1287     {
1288         Connection connection = null;
1289 
1290         try
1291         {
1292             connection = Transaction.begin((dbName == null)
1293                     ? Torque.getDefaultDB()
1294                     : dbName);
1295 
1296             List<TT> result = doSelect(
1297                     query,
1298                     mapper,
1299                     dbName,
1300                     connection);
1301 
1302             Transaction.commit(connection);
1303             connection = null;
1304             return result;
1305         }
1306         finally
1307         {
1308             if (connection != null)
1309             {
1310                 Transaction.safeRollback(connection);
1311             }
1312         }
1313     }
1314 
1315     /**
1316      * Selects rows from a database an maps them to objects.
1317      *
1318      * @param query the SQL Query to execute, not null.
1319      * @param mapper The mapper creating the objects from the resultSet,
1320      *        not null.
1321      * @param dbName The name of the database to create the connection for,
1322      *        or null for the default DB.
1323      * @param connection the database connection, not null.
1324      *
1325      * @return The results of the query, not null.
1326      *
1327      * @throws TorqueException if querying the database fails.
1328      */
1329     public <TT> List<TT> doSelect(
1330                 String query,
1331                 RecordMapper<TT> mapper,
1332                 String dbName,
1333                 Connection connection)
1334             throws TorqueException
1335     {
1336         if (connection == null)
1337         {
1338             throw new NullPointerException("connection is null");
1339         }
1340 
1341         List<TT> result = new ArrayList<TT>();
1342         Statement statement = null;
1343         ResultSet resultSet = null;
1344         try
1345         {
1346             statement = connection.createStatement();
1347             long startTime = System.currentTimeMillis();
1348             log.debug("Executing query " + query);
1349 
1350             resultSet = statement.executeQuery(query.toString());
1351             long queryEndTime = System.currentTimeMillis();
1352             log.trace("query took " + (queryEndTime - startTime)
1353                     + " milliseconds");
1354 
1355             while (resultSet.next())
1356             {
1357                 TT rowResult = mapper.processRow(resultSet, 0);
1358                 result.add(rowResult);
1359             }
1360             long mappingEndTime = System.currentTimeMillis();
1361             log.trace("mapping took " + (mappingEndTime - queryEndTime)
1362                     + " milliseconds");
1363         }
1364         catch (SQLException e)
1365         {
1366             final Adapter adapter = Torque.getAdapter(dbName);
1367             if (adapter != null)
1368             {
1369                 throw adapter.toTorqueException(e);
1370             }
1371             throw new TorqueException(e);
1372         }
1373         finally
1374         {
1375             if (resultSet != null)
1376             {
1377                 try
1378                 {
1379                     resultSet.close();
1380                 }
1381                 catch (SQLException e)
1382                 {
1383                     log.warn("error closing resultSet", e);
1384                 }
1385             }
1386             if (statement != null)
1387             {
1388                 try
1389                 {
1390                     statement.close();
1391                 }
1392                 catch (SQLException e)
1393                 {
1394                     log.warn("error closing statement", e);
1395                 }
1396             }
1397         }
1398         return result;
1399     }
1400 
1401     /**
1402      * Performs a SQL <code>select</code> using a PreparedStatement.
1403      *
1404      * @param criteria A Criteria specifying the records to select, not null.
1405      * @param mapper The mapper creating the objects from the resultSet,
1406      *        not null.
1407      * @param connection the database connection for selecting records,
1408      *        not null.
1409      *
1410      * @return The results of the query, not null.
1411      *
1412      * @throws TorqueException Error performing database query.
1413      *
1414      * @deprecated Please use doSelect(org.apache.torque.criteria.Criteria,
1415      *                 RecordMapper, Connection).
1416      *             This method will be removed in a future version of Torque.
1417      */
1418     @Deprecated
1419     public <TT> List<TT> doSelect(
1420             Criteria criteria,
1421             RecordMapper<TT> mapper,
1422             Connection connection)
1423         throws TorqueException
1424     {
1425         correctBooleans(criteria);
1426 
1427         Query query = SqlBuilder.buildQuery(criteria);
1428         if (query.getFromClause().isEmpty())
1429         {
1430             String tableName = SqlBuilder.getFullTableName(
1431                     getTableMap().getName(),
1432                     criteria.getDbName());
1433             query.getFromClause().add(new FromElement(tableName));
1434         }
1435 
1436         PreparedStatement statement = null;
1437         ResultSet resultSet = null;
1438         try
1439         {
1440             statement = connection.prepareStatement(query.toString());
1441 
1442             List<Object> replacements = setPreparedStatementReplacements(
1443                     statement,
1444                     query.getPreparedStatementReplacements(),
1445                     0);
1446 
1447             long startTime = System.currentTimeMillis();
1448             log.debug("Executing query " + query
1449                     + ", parameters = "
1450                     + replacements);
1451 
1452             resultSet = statement.executeQuery();
1453             long queryEndTime = System.currentTimeMillis();
1454             log.trace("query took " + (queryEndTime - startTime)
1455                     + " milliseconds");
1456 
1457             long offset;
1458             Database database = Torque.getDatabase(criteria.getDbName());
1459             if (database.getAdapter().supportsNativeOffset())
1460             {
1461                 offset = 0; //database takes care of offset
1462             }
1463             else
1464             {
1465                 offset = criteria.getOffset();
1466             }
1467 
1468             long limit;
1469             if (database.getAdapter().supportsNativeLimit())
1470             {
1471                 limit = -1; //database takes care of offset
1472             }
1473             else
1474             {
1475                 if (database.getAdapter().supportsNativeOffset())
1476                 {
1477                     limit = criteria.getLimit();
1478                 }
1479                 else
1480                 {
1481                     if (criteria.getLimit() == -1)
1482                     {
1483                         limit = criteria.getLimit();
1484                     }
1485                     else
1486                     {
1487                         limit = offset + criteria.getLimit();
1488                     }
1489                 }
1490             }
1491 
1492             List<TT> result = new ArrayList<TT>();
1493             int rowNumber = 0;
1494             while (resultSet.next())
1495             {
1496                 if (rowNumber < offset)
1497                 {
1498                     rowNumber++;
1499                     continue;
1500                 }
1501                 if (limit >= 0 && rowNumber >= limit)
1502                 {
1503                     break;
1504                 }
1505 
1506                 TT rowResult = mapper.processRow(resultSet, 0);
1507                 result.add(rowResult);
1508 
1509                 rowNumber++;
1510             }
1511             long mappingEndTime = System.currentTimeMillis();
1512             log.trace("mapping took " + (mappingEndTime - queryEndTime)
1513                     + " milliseconds");
1514 
1515             if (criteria.isSingleRecord() && result.size() > 1)
1516             {
1517                 throw new TooManyRowsException(
1518                         "Criteria expected single Record and "
1519                         + "Multiple Records were selected");
1520             }
1521             return result;
1522         }
1523         catch (SQLException e)
1524         {
1525             String dbName = criteria.getDbName();
1526             final Adapter adapter = Torque.getAdapter(dbName);
1527             if (adapter != null)
1528             {
1529                 throw adapter.toTorqueException(e);
1530             }
1531             throw new TorqueException(e);
1532         }
1533         finally
1534         {
1535             if (resultSet != null)
1536             {
1537                 try
1538                 {
1539                     resultSet.close();
1540                 }
1541                 catch (SQLException e)
1542                 {
1543                     log.warn("error closing resultSet", e);
1544                 }
1545             }
1546             if (statement != null)
1547             {
1548                 try
1549                 {
1550                     statement.close();
1551                 }
1552                 catch (SQLException e)
1553                 {
1554                     log.warn("error closing statement", e);
1555                 }
1556             }
1557         }
1558     }
1559 
1560     /**
1561      * Performs a SQL <code>select</code> using a PreparedStatement.
1562      *
1563      * @param criteria A Criteria specifying the records to select, not null.
1564      * @param mapper The mapper creating the objects from the resultSet,
1565      *        not null.
1566      * @param connection the database connection for selecting records,
1567      *        not null.
1568      *
1569      * @return The results of the query, not null.
1570      *
1571      * @throws TorqueException Error performing database query.
1572      */
1573     public <TT> List<TT> doSelect(
1574                 org.apache.torque.criteria.Criteria criteria,
1575                 RecordMapper<TT> mapper,
1576                 Connection connection)
1577             throws TorqueException
1578     {
1579         correctBooleans(criteria);
1580 
1581         Query query = SqlBuilder.buildQuery(criteria);
1582         if (query.getFromClause().isEmpty())
1583         {
1584             String tableName = SqlBuilder.getFullTableName(
1585                     getTableMap().getName(),
1586                     criteria.getDbName());
1587             query.getFromClause().add(new FromElement(tableName));
1588         }
1589 
1590         PreparedStatement statement = null;
1591         ResultSet resultSet = null;
1592         try
1593         {
1594             statement = connection.prepareStatement(query.toString());
1595             if (query.getFetchSize() != null)
1596             {
1597                 statement.setFetchSize(query.getFetchSize());
1598             }
1599 
1600             List<Object> replacements = setPreparedStatementReplacements(
1601                     statement,
1602                     query.getPreparedStatementReplacements(),
1603                     0);
1604 
1605             long startTime = System.currentTimeMillis();
1606             log.debug("Executing query " + query
1607                     + ", parameters = "
1608                     + replacements);
1609 
1610             resultSet = statement.executeQuery();
1611             long queryEndTime = System.currentTimeMillis();
1612             log.trace("query took " + (queryEndTime - startTime)
1613                     + " milliseconds");
1614 
1615             long offset;
1616             Database database = Torque.getDatabase(criteria.getDbName());
1617             if (database.getAdapter().supportsNativeOffset())
1618             {
1619                 offset = 0; //database takes care of offset
1620             }
1621             else
1622             {
1623                 offset = criteria.getOffset();
1624             }
1625 
1626             long limit;
1627             if (database.getAdapter().supportsNativeLimit())
1628             {
1629                 limit = -1; //database takes care of offset
1630             }
1631             else
1632             {
1633                 if (database.getAdapter().supportsNativeOffset())
1634                 {
1635                     limit = criteria.getLimit();
1636                 }
1637                 else
1638                 {
1639                     if (criteria.getLimit() == -1)
1640                     {
1641                         limit = criteria.getLimit();
1642                     }
1643                     else
1644                     {
1645                         limit = offset + criteria.getLimit();
1646                     }
1647                 }
1648             }
1649 
1650             List<TT> result = new ArrayList<TT>();
1651             int rowNumber = 0;
1652             while (resultSet.next())
1653             {
1654                 if (rowNumber < offset)
1655                 {
1656                     rowNumber++;
1657                     continue;
1658                 }
1659                 if (limit >= 0 && rowNumber >= limit)
1660                 {
1661                     break;
1662                 }
1663 
1664                 TT rowResult = mapper.processRow(resultSet, 0);
1665                 result.add(rowResult);
1666 
1667                 rowNumber++;
1668             }
1669             long mappingEndTime = System.currentTimeMillis();
1670             log.trace("mapping took " + (mappingEndTime - queryEndTime)
1671                     + " milliseconds");
1672 
1673             if (criteria.isSingleRecord() && result.size() > 1)
1674             {
1675                 throw new TooManyRowsException(
1676                         "Criteria expected single Record and "
1677                         + "Multiple Records were selected");
1678             }
1679             return result;
1680         }
1681         catch (SQLException e)
1682         {
1683             String dbName = criteria.getDbName();
1684             final Adapter adapter = Torque.getAdapter(dbName);
1685             if (adapter != null)
1686             {
1687                 throw adapter.toTorqueException(e);
1688             }
1689             throw new TorqueException(e);
1690         }
1691         finally
1692         {
1693             if (resultSet != null)
1694             {
1695                 try
1696                 {
1697                     resultSet.close();
1698                 }
1699                 catch (SQLException e)
1700                 {
1701                     log.warn("error closing resultSet", e);
1702                 }
1703             }
1704             if (statement != null)
1705             {
1706                 try
1707                 {
1708                     statement.close();
1709                 }
1710                 catch (SQLException e)
1711                 {
1712                     log.warn("error closing statement", e);
1713                 }
1714             }
1715         }
1716     }
1717 
1718     /**
1719      * Selects at most a single row from a database an maps them to objects.
1720      *
1721      * @param criteria A Criteria specifying the records to select, not null.
1722      * @param mapper The mapper creating the objects from the resultSet,
1723      *        not null.
1724      *
1725      * @return The selected row, or null if no records was selected.
1726      *
1727      * @throws TorqueException if querying the database fails.
1728      */
1729     public <TT> TT doSelectSingleRecord(
1730                 org.apache.torque.criteria.Criteria criteria,
1731                 RecordMapper<TT> mapper)
1732             throws TorqueException
1733     {
1734         List<TT> resultList = doSelect(criteria, mapper);
1735         TT result = null;
1736         if (resultList.size() > 1)
1737         {
1738             throw new TooManyRowsException("Criteria " + criteria
1739                 + " matched more than one record");
1740         }
1741         if (!resultList.isEmpty())
1742         {
1743             result = resultList.get(0);
1744         }
1745         return result;
1746     }
1747 
1748     /**
1749      * Selects at most a single row from a database an maps them to objects.
1750      *
1751      * @param criteria A Criteria specifying the records to select, not null.
1752      * @param mapper The mapper creating the objects from the resultSet,
1753      *        not null.
1754      * @param connection the database connection, not null.
1755      *
1756      * @return The selected row, or null if no records was selected.
1757      *
1758      * @throws TorqueException if querying the database fails.
1759      */
1760     public <TT> TT doSelectSingleRecord(
1761                 org.apache.torque.criteria.Criteria criteria,
1762                 RecordMapper<TT> mapper,
1763                 Connection connection)
1764             throws TorqueException
1765     {
1766         List<TT> resultList = doSelect(
1767                 criteria,
1768                 mapper,
1769                 connection);
1770         TT result = null;
1771         if (resultList.size() > 1)
1772         {
1773             throw new TooManyRowsException("Criteria " + criteria
1774                 + " matched more than one record");
1775         }
1776         if (!resultList.isEmpty())
1777         {
1778             result = resultList.get(0);
1779         }
1780         return result;
1781     }
1782 
1783     /**
1784      * Convenience method used to update rows in the DB.  Checks if a
1785      * <i>single</i> primary key is specified in the Criteria
1786      * object and uses it to perform the update.  If no primary key is
1787      * specified or the table has multiple primary keys,
1788      * an Exception will be thrown.
1789      * <p>
1790      * Use this method for performing an update of the kind:
1791      * <p>
1792      * "WHERE primary_key_id = someValue"
1793      * <p>
1794      * To perform an update on a table with multiple primary keys or
1795      * an update with non-primary key fields in the WHERE
1796      * clause, use doUpdate(ColumnValues, Criteria).
1797      *
1798      * @param updateValues Which columns to update with which values
1799      *        for which primary key value, not null.
1800      *
1801      * @return the number of affected rows.
1802      *
1803      * @throws TorqueException Any exceptions caught during processing will be
1804      *         rethrown wrapped into a TorqueException.
1805      */
1806     public int doUpdate(ColumnValues updateValues)
1807             throws TorqueException
1808     {
1809         String databaseNameFromUpdateValues = updateValues.getDbName();
1810         if (databaseNameFromUpdateValues == null)
1811         {
1812             databaseNameFromUpdateValues = getDatabaseName();
1813         }
1814         Connection connection = null;
1815         try
1816         {
1817             connection = Transaction.begin(databaseNameFromUpdateValues);
1818             int result = doUpdate(updateValues, connection);
1819             Transaction.commit(connection);
1820             connection = null;
1821             return result;
1822         }
1823         finally
1824         {
1825             if (connection != null)
1826             {
1827                 Transaction.safeRollback(connection);
1828             }
1829         }
1830     }
1831 
1832     /**
1833      * Convenience method used to update rows in the DB.  Checks if a
1834      * <i>single</i> primary key is specified in the Criteria
1835      * object and uses it to perform the update.  If no primary key is
1836      * specified or the table has multiple primary keys,
1837      * an Exception will be thrown.
1838      * <p>
1839      * Use this method for performing an update of the kind:
1840      * <p>
1841      * "WHERE primary_key_id = someValue"
1842      * <p>
1843      * To perform an update on a table with multiple primary keys or
1844      * an update with non-primary key fields in the WHERE
1845      * clause, use doUpdate(ColumnValues, Criteria, Connection).
1846      *
1847      * @param updateValues Which columns to update with which values
1848      *        for which primary key value, not null.
1849      * @param connection the database connection to use.
1850      *
1851      * @return the number of affected rows.
1852      *
1853      * @throws TorqueException Any exceptions caught during processing will be
1854      *         rethrown wrapped into a TorqueException.
1855      */
1856     public int doUpdate(
1857                 ColumnValues updateValues,
1858                 Connection connection)
1859             throws TorqueException
1860     {
1861         ColumnMap pk = getTableMap().getPrimaryKey();
1862         org.apache.torque.criteria.Criteria selectCriteria = null;
1863 
1864         if (pk != null && updateValues.containsKey(pk.getSqlExpression()))
1865         {
1866             selectCriteria = new org.apache.torque.criteria.Criteria();
1867             selectCriteria.where(pk,
1868                 updateValues.remove(pk.getSqlExpression()));
1869         }
1870         else
1871         {
1872             throw new TorqueException("No PK specified for database update");
1873         }
1874 
1875         return doUpdate(selectCriteria, updateValues, connection);
1876     }
1877 
1878     /**
1879      * Executes an update against the database. The rows to be updated
1880      * are selected using <code>criteria</code> and updated using the values
1881      * in <code>updateValues</code>.
1882      *
1883      * @param selectCriteria selects which rows of which table
1884      *        should be updated, not null.
1885      * @param updateValues Which columns to update with which values, not null.
1886      *
1887      * @return the number of affected rows.
1888      *
1889      * @throws TorqueException if updating fails.
1890      *
1891      * @deprecated Please use doUpdate(
1892      *                 org.apache.torque.criteria.Criteria, ColumnValues).
1893      *             This method will be removed in a future version of Torque.
1894      */
1895     @Deprecated
1896     public int doUpdate(
1897                 Criteria selectCriteria,
1898                 ColumnValues updateValues)
1899             throws TorqueException
1900     {
1901         String databaseNameFromUpdateValues = updateValues.getDbName();
1902         if (databaseNameFromUpdateValues == null)
1903         {
1904             databaseNameFromUpdateValues = getDatabaseName();
1905         }
1906         Connection connection = null;
1907         try
1908         {
1909             connection = Transaction.begin(databaseNameFromUpdateValues);
1910             int result = doUpdate(selectCriteria, updateValues, connection);
1911             Transaction.commit(connection);
1912             connection = null;
1913             return result;
1914         }
1915         finally
1916         {
1917             if (connection != null)
1918             {
1919                 Transaction.safeRollback(connection);
1920             }
1921         }
1922     }
1923 
1924     /**
1925      * Executes an update against the database. The rows to be updated
1926      * are selected using <code>criteria</code> and updated using the values
1927      * in <code>updateValues</code>.
1928      *
1929      * @param selectCriteria selects which rows of which table
1930      *        should be updated, not null.
1931      * @param updateValues Which columns to update with which values, not null.
1932      *
1933      * @return the number of affected rows.
1934      *
1935      * @throws TorqueException if updating fails.
1936      */
1937     public int doUpdate(
1938                 org.apache.torque.criteria.Criteria selectCriteria,
1939                 ColumnValues updateValues)
1940             throws TorqueException
1941     {
1942         String databaseNameFromUpdateValues = updateValues.getDbName();
1943         if (databaseNameFromUpdateValues == null)
1944         {
1945             databaseNameFromUpdateValues = getDatabaseName();
1946         }
1947         Connection connection = null;
1948         try
1949         {
1950             connection = Transaction.begin(databaseNameFromUpdateValues);
1951             int result = doUpdate(selectCriteria, updateValues, connection);
1952             Transaction.commit(connection);
1953             connection = null;
1954             return result;
1955         }
1956         finally
1957         {
1958             if (connection != null)
1959             {
1960                 Transaction.safeRollback(connection);
1961             }
1962         }
1963     }
1964 
1965     /**
1966      * Executes an update against the database. The rows to be updated
1967      * are selected using <code>criteria</code> and updated using the values
1968      * in <code>updateValues</code>.
1969      *
1970      * @param criteria selects which rows of which table should be updated.
1971      * @param updateValues Which columns to update with which values, not null.
1972      * @param connection the database connection to use, not null.
1973      *
1974      * @return the number of affected rows.
1975      *
1976      * @throws TorqueException if updating fails.
1977      *
1978      * @deprecated Please use doUpdate(org.apache.torque.criteria.Criteria,
1979      *                 ColumnValues, Connection).
1980      *             This method will be removed in a future version of Torque.
1981      */
1982     @Deprecated
1983     public int doUpdate(
1984                 Criteria criteria,
1985                 ColumnValues updateValues,
1986                 Connection connection)
1987             throws TorqueException
1988     {
1989         Query query = SqlBuilder.buildQuery(criteria);
1990         query.setType(Query.Type.UPDATE);
1991 
1992         query.getFromClause().clear();
1993         String fullTableName = SqlBuilder.getFullTableName(
1994                 getTableMap().getName(),
1995                 criteria.getDbName());
1996         query.getFromClause().add(new FromElement(fullTableName));
1997 
1998         List<JdbcTypedValue> replacementObjects
1999                 = new ArrayList<JdbcTypedValue>();
2000         for (Map.Entry<Column, JdbcTypedValue> updateValue
2001                 : updateValues.entrySet())
2002         {
2003             Column column = updateValue.getKey();
2004             query.getSelectClause().add(column.getColumnName());
2005             replacementObjects.add(updateValue.getValue());
2006         }
2007 
2008         PreparedStatement preparedStatement = null;
2009         try
2010         {
2011             preparedStatement = connection.prepareStatement(query.toString());
2012             int position = 1;
2013             for (JdbcTypedValue replacementObject : replacementObjects)
2014             {
2015                 Object value = replacementObject.getValue();
2016                 if (value != null)
2017                 {
2018                     preparedStatement.setObject(position, value);
2019                 }
2020                 else
2021                 {
2022                     preparedStatement.setNull(
2023                             position,
2024                             replacementObject.getJdbcType());
2025                 }
2026                 position++;
2027             }
2028             List<Object> replacements = setPreparedStatementReplacements(
2029                     preparedStatement,
2030                     query.getPreparedStatementReplacements(),
2031                     position - 1);
2032             long startTime = System.currentTimeMillis();
2033             log.debug("Executing update " + query.toString()
2034                     + " using update parameters " + replacementObjects
2035                     + " and query parameters "
2036                     + replacements);
2037 
2038             int affectedRows = preparedStatement.executeUpdate();
2039             long queryEndTime = System.currentTimeMillis();
2040             log.trace("update took " + (queryEndTime - startTime)
2041                     + " milliseconds");
2042 
2043             preparedStatement.close();
2044             preparedStatement = null;
2045             return affectedRows;
2046         }
2047         catch (SQLException e)
2048         {
2049             String dbName = updateValues.getDbName();
2050             final Adapter adapter = Torque.getAdapter(dbName);
2051             if (adapter != null)
2052             {
2053                 throw adapter.toTorqueException(e);
2054             }
2055             throw new TorqueException(e);
2056         }
2057         finally
2058         {
2059             if (preparedStatement != null)
2060             {
2061                 try
2062                 {
2063                     preparedStatement.close();
2064                 }
2065                 catch (SQLException e)
2066                 {
2067                     log.warn("error closing prepared statement", e);
2068                 }
2069             }
2070         }
2071     }
2072 
2073     /**
2074      * Executes an update against the database. The rows to be updated
2075      * are selected using <code>criteria</code> and updated using the values
2076      * in <code>updateValues</code>.
2077      *
2078      * @param criteria selects which rows of which table should be updated.
2079      * @param updateValues Which columns to update with which values, not null.
2080      * @param connection the database connection to use, not null.
2081      *
2082      * @return the number of affected rows.
2083      *
2084      * @throws TorqueException if updating fails.
2085      */
2086     public int doUpdate(
2087                 org.apache.torque.criteria.Criteria criteria,
2088                 ColumnValues updateValues,
2089                 Connection connection)
2090             throws TorqueException
2091     {
2092         Query query = SqlBuilder.buildQuery(criteria);
2093         query.setType(Query.Type.UPDATE);
2094 
2095         query.getFromClause().clear();
2096         String fullTableName = SqlBuilder.getFullTableName(
2097                 getTableMap().getName(),
2098                 criteria.getDbName());
2099         query.getFromClause().add(new FromElement(fullTableName));
2100 
2101         List<JdbcTypedValue> replacementObjects
2102                 = new ArrayList<JdbcTypedValue>();
2103         for (Map.Entry<Column, JdbcTypedValue> updateValue
2104                 : updateValues.entrySet())
2105         {
2106             Column column = updateValue.getKey();
2107             query.getSelectClause().add(column.getColumnName());
2108             replacementObjects.add(updateValue.getValue());
2109         }
2110 
2111         PreparedStatement preparedStatement = null;
2112         try
2113         {
2114             preparedStatement = connection.prepareStatement(query.toString());
2115             int position = 1;
2116             for (JdbcTypedValue replacementObject : replacementObjects)
2117             {
2118                 Object value = replacementObject.getValue();
2119                 if (value != null)
2120                 {
2121                     preparedStatement.setObject(position, value);
2122                 }
2123                 else
2124                 {
2125                     preparedStatement.setNull(
2126                             position,
2127                             replacementObject.getJdbcType());
2128                 }
2129                 position++;
2130             }
2131             List<Object> replacements = setPreparedStatementReplacements(
2132                     preparedStatement,
2133                     query.getPreparedStatementReplacements(),
2134                     position - 1);
2135             long startTime = System.currentTimeMillis();
2136             log.debug("Executing update " + query.toString()
2137                     + " using update parameters " + replacementObjects
2138                     + " and query parameters "
2139                     + replacements);
2140 
2141             int affectedRows = preparedStatement.executeUpdate();
2142             long queryEndTime = System.currentTimeMillis();
2143             log.trace("update took " + (queryEndTime - startTime)
2144                     + " milliseconds");
2145 
2146             preparedStatement.close();
2147             preparedStatement = null;
2148             return affectedRows;
2149         }
2150         catch (SQLException e)
2151         {
2152             final String dbName = updateValues.getDbName();
2153             final Adapter adapter = Torque.getAdapter(dbName);
2154             if (adapter != null)
2155             {
2156                 throw adapter.toTorqueException(e);
2157             }
2158             throw new TorqueException(e);
2159         }
2160         finally
2161         {
2162             if (preparedStatement != null)
2163             {
2164                 try
2165                 {
2166                     preparedStatement.close();
2167                 }
2168                 catch (SQLException e)
2169                 {
2170                     log.warn("error closing prepared statement", e);
2171                 }
2172             }
2173         }
2174     }
2175 
2176     /**
2177      * Utility method which executes a given sql statement.  This
2178      * method should be used for update, insert, and delete
2179      * statements.  Use executeQuery() for selects.
2180      *
2181      * @param statementString A String with the sql statement to execute.
2182      * @return The number of rows affected.
2183      * @throws TorqueException Any exceptions caught during processing will be
2184      *         rethrown wrapped into a TorqueException.
2185      */
2186     public int executeStatement(String statementString) throws TorqueException
2187     {
2188         return executeStatement(statementString, Torque.getDefaultDB());
2189     }
2190 
2191     /**
2192      * Utility method which executes a given sql statement.  This
2193      * method should be used for update, insert, and delete
2194      * statements.  Use executeQuery() for selects.
2195      *
2196      * @param statementString A String with the sql statement to execute.
2197      * @param dbName The name of the database to execute the statement against,
2198      *        or null for the default DB.
2199      *
2200      * @return The number of rows affected.
2201      *
2202      * @throws TorqueException Any exceptions caught during processing will be
2203      *         rethrown wrapped into a TorqueException.
2204      */
2205     public int executeStatement(String statementString, String dbName)
2206         throws TorqueException
2207     {
2208         Connection con = null;
2209         try
2210         {
2211             con = Transaction.begin(dbName);
2212             int rowCount = executeStatement(statementString, dbName,con);
2213             Transaction.commit(con);
2214             con = null;
2215             return rowCount;
2216         }
2217         finally
2218         {
2219             if (con != null)
2220             {
2221                 Transaction.safeRollback(con);
2222             }
2223         }
2224     }
2225 
2226     /**
2227      * Utility method which executes a given sql statement.  This
2228      * method should be used for update, insert, and delete
2229      * statements.  Use executeQuery() for selects.
2230      *
2231      * @param statementString A String with the sql statement to execute.
2232      * @param dbName The name of the database to execute the statement against,
2233      *        or null for the default DB.
2234      * @param con The database connection to use.
2235      *
2236      * @return The number of rows affected.
2237      *
2238      * @throws TorqueException Any exceptions caught during processing will be
2239      *         rethrown wrapped into a TorqueException.
2240      */
2241     public int executeStatement(
2242             String statementString,
2243             String dbName,
2244             Connection con)
2245         throws TorqueException
2246     {
2247         int rowCount = -1;
2248         Statement statement = null;
2249         try
2250         {
2251             statement = con.createStatement();
2252             rowCount = statement.executeUpdate(statementString);
2253         }
2254         catch (SQLException e)
2255         {
2256             throw new TorqueException(e);
2257         }
2258         finally
2259         {
2260             if (statement != null)
2261             {
2262                 try
2263                 {
2264                     statement.close();
2265                 }
2266                 catch (SQLException e)
2267                 {
2268                     throw new TorqueException(e);
2269                 }
2270             }
2271         }
2272         return rowCount;
2273     }
2274 
2275     /**
2276      * Sets the prepared statement replacements into a query, possibly
2277      * modifying the type if required by DB Drivers.
2278      *
2279      * @param statement the statement to set the parameters in, not null.
2280      * @param replacements the replacements to set, not null.
2281      * @param offset the offset on the parameters, 0 for no offset.
2282      *
2283      * @return the parameters set.
2284      *
2285      * @throws SQLException if setting the parameter fails.
2286      */
2287     private List<Object> setPreparedStatementReplacements(
2288                 PreparedStatement statement,
2289                 List<Object> replacements,
2290                 int offset)
2291             throws SQLException
2292     {
2293         List<Object> result = new ArrayList<Object>(replacements.size());
2294         int i = 1 + offset;
2295         for (Object param : replacements)
2296         {
2297             if (param instanceof java.sql.Timestamp)
2298             {
2299                 statement.setTimestamp(i, (java.sql.Timestamp) param);
2300                 result.add(param);
2301             }
2302             else if (param instanceof java.sql.Date)
2303             {
2304                 statement.setDate(i, (java.sql.Date) param);
2305                 result.add(param);
2306             }
2307             else if (param instanceof java.util.Date)
2308             {
2309                 java.sql.Timestamp sqlDate = new java.sql.Timestamp(
2310                         ((java.util.Date) param).getTime());
2311                 statement.setTimestamp(i, sqlDate);
2312                 result.add(sqlDate);
2313             }
2314             else if (param instanceof NumberKey)
2315             {
2316                 BigDecimal bigDecimal = ((NumberKey) param).getBigDecimal();
2317                 statement.setBigDecimal(i, bigDecimal);
2318                 result.add(bigDecimal);
2319             }
2320             else if (param instanceof Integer)
2321             {
2322                 statement.setInt(i, ((Integer) param).intValue());
2323                 result.add(param);
2324             }
2325             else if (param instanceof Long)
2326             {
2327                 statement.setLong(i, ((Long) param).longValue());
2328                 result.add(param);
2329             }
2330             else if (param instanceof BigDecimal)
2331             {
2332                 statement.setBigDecimal(i, (BigDecimal) param);
2333                 result.add(param);
2334             }
2335             else if (param instanceof Boolean)
2336             {
2337                 statement.setBoolean(i, ((Boolean) param).booleanValue());
2338                 result.add(param);
2339             }
2340             else
2341             {
2342                 statement.setString(i, param.toString());
2343                 result.add(param.toString());
2344             }
2345             ++i;
2346         }
2347         return result;
2348     }
2349 
2350     /**
2351      * Changes the boolean values in the criteria to the appropriate type,
2352      * whenever a booleanchar or booleanint column is involved.
2353      * This enables the user to create criteria using Boolean values
2354      * for booleanchar or booleanint columns.
2355      *
2356      * @param criteria the criteria in which the boolean values should be
2357      *        corrected.
2358      * @throws TorqueException if the database map for the criteria cannot be
2359      *         obtained.
2360      *
2361      * @deprecated Please use correctBooleans(
2362      *                 org.apache.torque.criteria.Criteria).
2363      *             This method will be removed in a future version of Torque.
2364      */
2365     @Deprecated
2366     public void correctBooleans(Criteria criteria)
2367             throws TorqueException
2368     {
2369         for (Object criterionObject : criteria.values())
2370         {
2371             Criteria.Criterion criterion = (Criteria.Criterion) criterionObject;
2372             correctBooleans(criteria, criterion);
2373        }
2374     }
2375 
2376     /**
2377      * Checks all columns in the criteria to see whether
2378      * booleanchar and booleanint columns are queried with a boolean.
2379      * If yes, the query values are mapped onto values the database
2380      * does understand, i.e. 0 and 1 for booleanints and N and Y for
2381      * booleanchar columns.
2382      *
2383      * @param criteria The criteria to which teh criterion belongs.
2384      * @param criterion The criterion to be checked for booleanint
2385      *        and booleanchar columns.
2386      *
2387      * @throws TorqueException if the database map for the criteria cannot be
2388      *         retrieved.
2389      *
2390      * @deprecated
2391      */
2392     @Deprecated
2393     private void correctBooleans(
2394                 Criteria criteria,
2395                 Criteria.Criterion criterion)
2396             throws TorqueException
2397     {
2398         Column column = criterion.getColumn();
2399         TableMap tableMapFromCriteria = MapHelper.getTableMap(
2400                 column,
2401                 criteria,
2402                 tableMap);
2403         // if no description of table available, do not modify anything
2404         if (tableMapFromCriteria != null)
2405         {
2406             String columnName = column.getColumnName();
2407             ColumnMap columnMap = tableMapFromCriteria.getColumn(columnName);
2408             if (columnMap != null)
2409             {
2410                 if ("BOOLEANINT".equals(columnMap.getTorqueType()))
2411                 {
2412                     replaceBooleanValues(
2413                             criterion,
2414                             Integer.valueOf(1),
2415                             Integer.valueOf(0));
2416                 }
2417                 else if ("BOOLEANCHAR".equals(columnMap.getTorqueType()))
2418                 {
2419                     replaceBooleanValues(criterion, "Y", "N");
2420                  }
2421             }
2422         }
2423         for (Criteria.Criterion attachedCriterion : criterion.getClauses())
2424         {
2425             correctBooleans(criteria, attachedCriterion);
2426         }
2427     }
2428 
2429     /**
2430      * Checks all columns in the criteria to see whether
2431      * booleanchar and booleanint columns are queried with a boolean.
2432      * If yes, the query values are mapped onto values the database
2433      * does understand, i.e. 0 and 1 for booleanints and N and Y for
2434      * booleanchar columns.
2435      *
2436      * @param criteria The criteria to be checked for booleanint and booleanchar
2437      *        columns.
2438      *
2439      * @throws TorqueException if the database map for the criteria cannot be
2440      *         retrieved.
2441      */
2442     public void correctBooleans(
2443                 org.apache.torque.criteria.Criteria criteria)
2444             throws TorqueException
2445     {
2446        correctBooleans(
2447                criteria,
2448                criteria.getTopLevelCriterion());
2449     }
2450 
2451     private void correctBooleans(
2452                 org.apache.torque.criteria.Criteria criteria,
2453                 org.apache.torque.criteria.Criterion criterion)
2454             throws TorqueException
2455     {
2456         if (criterion == null)
2457         {
2458             return;
2459         }
2460         if (criterion.isComposite())
2461         {
2462             for (org.apache.torque.criteria.Criterion part
2463                     : criterion.getParts())
2464             {
2465                 correctBooleans(criteria, part);
2466             }
2467             return;
2468         }
2469 
2470         Object possibleColumn = criterion.getLValue();
2471         TableMap tableMapForColumn = MapHelper.getTableMap(
2472                 possibleColumn,
2473                 criteria,
2474                 tableMap);
2475         // if no description of table available, do not modify anything
2476         if (tableMapForColumn == null)
2477         {
2478             return;
2479         }
2480         String columnName = ((Column) possibleColumn).getColumnName();
2481         ColumnMap columnMap = tableMapForColumn.getColumn(columnName);
2482         if (columnMap != null)
2483         {
2484             if ("BOOLEANINT".equals(columnMap.getTorqueType()))
2485             {
2486                 replaceBooleanValues(
2487                         criterion,
2488                         Integer.valueOf(1),
2489                         Integer.valueOf(0));
2490             }
2491             else if ("BOOLEANCHAR".equals(columnMap.getTorqueType()))
2492             {
2493                 replaceBooleanValues(criterion, "Y", "N");
2494              }
2495         }
2496     }
2497 
2498     /**
2499      * Replaces any Boolean value in the criterion and its attached Criterions
2500      * by trueValue if the Boolean equals <code>Boolean.TRUE</code>
2501      * and falseValue if the Boolean equals <code>Boolean.FALSE</code>.
2502      *
2503      * @param criterion the criterion to replace Boolean values in.
2504      * @param trueValue the value by which Boolean.TRUE should be replaced.
2505      * @param falseValue the value by which Boolean.FALSE should be replaced.
2506      *
2507      * @deprecated
2508      */
2509     @Deprecated
2510     private void replaceBooleanValues(
2511             Criteria.Criterion criterion,
2512             Object trueValue,
2513             Object falseValue)
2514     {
2515         // attachedCriterions also contains the criterion itself,
2516         // so no additional treatment is needed for the criterion itself.
2517         Criteria.Criterion[] attachedCriterions
2518             = criterion.getAttachedCriterion();
2519         for (int i = 0; i < attachedCriterions.length; ++i)
2520         {
2521             Object criterionValue
2522                     = attachedCriterions[i].getValue();
2523             if (criterionValue instanceof Boolean)
2524             {
2525                 Boolean booleanValue = (Boolean) criterionValue;
2526                 attachedCriterions[i].setValue(
2527                         Boolean.TRUE.equals(booleanValue)
2528                                 ? trueValue
2529                                 : falseValue);
2530             }
2531 
2532         }
2533     }
2534 
2535     /**
2536      * Replaces any Boolean value in the criterion and its attached Criterions
2537      * by trueValue if the Boolean equals <code>Boolean.TRUE</code>
2538      * and falseValue if the Boolean equals <code>Boolean.FALSE</code>.
2539      *
2540      * @param criterion the criterion to replace Boolean values in.
2541      *        May not be a composite criterion.
2542      * @param trueValue the value by which Boolean.TRUE should be replaced.
2543      * @param falseValue the value by which Boolean.FALSE should be replaced.
2544      */
2545     private void replaceBooleanValues(
2546             org.apache.torque.criteria.Criterion criterion,
2547             Object trueValue,
2548             Object falseValue)
2549     {
2550         Object rValue = criterion.getRValue();
2551         if (rValue instanceof Boolean)
2552         {
2553             Boolean booleanValue = (Boolean) rValue;
2554             criterion.setRValue(
2555                     Boolean.TRUE.equals(booleanValue)
2556                             ? trueValue
2557                             : falseValue);
2558         }
2559         Object lValue = criterion.getLValue();
2560         if (lValue instanceof Boolean)
2561         {
2562             Boolean booleanValue = (Boolean) lValue;
2563             criterion.setLValue(
2564                     Boolean.TRUE.equals(booleanValue)
2565                             ? trueValue
2566                             : falseValue);
2567         }
2568     }
2569 
2570     /**
2571      * Checks all columns in the criteria to see whether
2572      * booleanchar and booleanint columns are queried with a boolean.
2573      * If yes, the query values are mapped onto values the database
2574      * does understand, i.e. 0 and 1 for booleanints and N and Y for
2575      * booleanchar columns.
2576      *
2577      * @param columnValues The value to be checked for booleanint
2578      *        and booleanchar columns.
2579      * @throws TorqueException if the database map for the criteria cannot be
2580      *         retrieved.
2581      */
2582     public void correctBooleans(
2583             ColumnValues columnValues)
2584         throws TorqueException
2585     {
2586         for (Map.Entry<Column, JdbcTypedValue> entry : columnValues.entrySet())
2587         {
2588             String columnName = entry.getKey().getColumnName();
2589             ColumnMap column = getTableMap().getColumn(columnName);
2590             if (column != null)
2591             {
2592                 JdbcTypedValue columnValue = entry.getValue();
2593                 if ("BOOLEANINT".equals(column.getTorqueType()))
2594                 {
2595                     if (Boolean.TRUE.equals(columnValue.getValue()))
2596                     {
2597                         entry.setValue(new JdbcTypedValue(1, Types.INTEGER));
2598                     }
2599                     else if (Boolean.FALSE.equals(columnValue.getValue()))
2600                     {
2601                         entry.setValue(new JdbcTypedValue(0, Types.INTEGER));
2602                     }
2603                     else if (columnValue.getValue() == null)
2604                     {
2605                         entry.setValue(new JdbcTypedValue(null, Types.INTEGER));
2606                     }
2607                 }
2608                 else if ("BOOLEANCHAR".equals(column.getTorqueType()))
2609                 {
2610                     if (Boolean.TRUE.equals(columnValue.getValue()))
2611                     {
2612                         entry.setValue(new JdbcTypedValue("Y", Types.CHAR));
2613                     }
2614                     else if (Boolean.FALSE.equals(columnValue.getValue()))
2615                     {
2616                         entry.setValue(new JdbcTypedValue("N", Types.CHAR));
2617                     }
2618                     else if (columnValue.getValue() == null)
2619                     {
2620                         entry.setValue(new JdbcTypedValue(null, Types.CHAR));
2621                     }
2622                  }
2623             }
2624         }
2625     }
2626 
2627     /**
2628      * Sets the database name in the passed criteria to the table's default,
2629      * if it is not already set.
2630      *
2631      * @param crit the criteria to set the database name in, not null.
2632      */
2633     protected void setDbName(org.apache.torque.criteria.Criteria crit)
2634             throws TorqueException
2635     {
2636         if (crit.getDbName() == null)
2637         {
2638             crit.setDbName(getDatabaseName());
2639         }
2640     }
2641 
2642     /**
2643      * Sets the database name in the passed criteria to the table's default,
2644      * if it is not already set.
2645      *
2646      * @param crit the criteria to set the database name in, not null.
2647      *
2648      * @deprecated Please use addSelectColumns(
2649      *                 org.apache.torque.criteria.Criteria).
2650      *             This method will be removed in a future version of Torque.
2651      */
2652     @Deprecated
2653     protected void setDbName(Criteria crit) throws TorqueException
2654     {
2655         if (crit.getDbName() == null)
2656         {
2657             crit.setDbName(getDatabaseName());
2658         }
2659     }
2660 }