View Javadoc
1 package org.apache.torque.util; 2 3 /* ==================================================================== 4 * The Apache Software License, Version 1.1 5 * 6 * Copyright (c) 2001-2003 The Apache Software Foundation. All rights 7 * reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The end-user documentation included with the redistribution, 22 * if any, must include the following acknowledgment: 23 * "This product includes software developed by the 24 * Apache Software Foundation (http://www.apache.org/)." 25 * Alternately, this acknowledgment may appear in the software itself, 26 * if and wherever such third-party acknowledgments normally appear. 27 * 28 * 4. The names "Apache" and "Apache Software Foundation" and 29 * "Apache Turbine" must not be used to endorse or promote products 30 * derived from this software without prior written permission. For 31 * written permission, please contact apache@apache.org. 32 * 33 * 5. Products derived from this software may not be called "Apache", 34 * "Apache Turbine", nor may "Apache" appear in their name, without 35 * prior written permission of the Apache Software Foundation. 36 * 37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This software consists of voluntary contributions made by many 52 * individuals on behalf of the Apache Software Foundation. For more 53 * information on the Apache Software Foundation, please see 54 * <http://www.apache.org/>. 55 */ 56 57 import java.io.BufferedOutputStream; 58 import java.io.ByteArrayOutputStream; 59 import java.io.IOException; 60 import java.io.ObjectOutputStream; 61 import java.io.Serializable; 62 import java.math.BigDecimal; 63 import java.sql.Connection; 64 import java.sql.PreparedStatement; 65 import java.sql.SQLException; 66 import java.sql.Statement; 67 import java.util.ArrayList; 68 import java.util.Collections; 69 import java.util.HashSet; 70 import java.util.Hashtable; 71 import java.util.Iterator; 72 import java.util.List; 73 74 import org.apache.commons.lang.StringUtils; 75 76 import org.apache.commons.logging.Log; 77 import org.apache.commons.logging.LogFactory; 78 79 import org.apache.torque.Torque; 80 import org.apache.torque.TorqueException; 81 import org.apache.torque.adapter.DB; 82 import org.apache.torque.map.ColumnMap; 83 import org.apache.torque.map.DatabaseMap; 84 import org.apache.torque.map.MapBuilder; 85 import org.apache.torque.map.TableMap; 86 import org.apache.torque.oid.IdGenerator; 87 import org.apache.torque.om.NumberKey; 88 import org.apache.torque.om.ObjectKey; 89 import org.apache.torque.om.SimpleKey; 90 import org.apache.torque.om.StringKey; 91 92 import com.workingdogs.village.Column; 93 import com.workingdogs.village.DataSet; 94 import com.workingdogs.village.KeyDef; 95 import com.workingdogs.village.QueryDataSet; 96 import com.workingdogs.village.Record; 97 import com.workingdogs.village.Schema; 98 import com.workingdogs.village.TableDataSet; 99 100 /*** 101 * This is the base class for all Peer classes in the system. Peer 102 * classes are responsible for isolating all of the database access 103 * for a specific business object. They execute all of the SQL 104 * against the database. Over time this class has grown to include 105 * utility methods which ease execution of cross-database queries and 106 * the implementation of concrete Peers. 107 * 108 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a> 109 * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a> 110 * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a> 111 * @author <a href="mailto:stephenh@chase3000.com">Stephen Haberman</a> 112 * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a> 113 * @version $Id: BasePeer.java,v 1.76 2003/08/25 16:33:22 henning Exp $ 114 */ 115 public abstract class BasePeer implements java.io.Serializable 116 { 117 /*** Constant criteria key to reference ORDER BY columns. */ 118 public static final String ORDER_BY = "ORDER BY"; 119 120 /*** 121 * Constant criteria key to remove Case Information from 122 * search/ordering criteria. 123 */ 124 public static final String IGNORE_CASE = "IgNOrE cAsE"; 125 126 /*** Classes that implement this class should override this value. */ 127 public static final String TABLE_NAME = "TABLE_NAME"; 128 129 /*** 130 * The Torque default MapBuilder. 131 * 132 * @deprecated there is no default map builder! 133 */ 134 public static final String DEFAULT_MAP_BUILDER = 135 "org.apache.torque.util.db.map.TurbineMapBuilder"; 136 137 /*** Hashtable that contains the cached mapBuilders. */ 138 private static Hashtable mapBuilders = new Hashtable(5); 139 140 /*** the log */ 141 protected static Log log = LogFactory.getLog(BasePeer.class); 142 143 /*** 144 * Converts a hashtable to a byte array for storage/serialization. 145 * 146 * @param hash The Hashtable to convert. 147 * @return A byte[] with the converted Hashtable. 148 * @throws TorqueException Any exceptions caught during processing will be 149 * rethrown wrapped into a TorqueException. 150 */ 151 public static byte[] hashtableToByteArray(Hashtable hash) 152 throws TorqueException 153 { 154 Hashtable saveData = new Hashtable(hash.size()); 155 String key = null; 156 Object value = null; 157 byte[] byteArray = null; 158 159 Iterator keys = hash.keySet().iterator(); 160 while (keys.hasNext()) 161 { 162 key = (String) keys.next(); 163 value = hash.get(key); 164 if (value instanceof Serializable) 165 { 166 saveData.put(key, value); 167 } 168 } 169 170 ByteArrayOutputStream baos = null; 171 BufferedOutputStream bos = null; 172 ObjectOutputStream out = null; 173 try 174 { 175 // These objects are closed in the finally. 176 baos = new ByteArrayOutputStream(); 177 bos = new BufferedOutputStream(baos); 178 out = new ObjectOutputStream(bos); 179 180 out.writeObject(saveData); 181 out.flush(); 182 bos.flush(); 183 byteArray = baos.toByteArray(); 184 } 185 catch (Exception e) 186 { 187 throwTorqueException(e); 188 } 189 finally 190 { 191 if (out != null) 192 { 193 try 194 { 195 out.close(); 196 } 197 catch (IOException ignored) 198 { 199 } 200 } 201 202 if (bos != null) 203 { 204 try 205 { 206 bos.close(); 207 } 208 catch (IOException ignored) 209 { 210 } 211 } 212 213 if (baos != null) 214 { 215 try 216 { 217 baos.close(); 218 } 219 catch (IOException ignored) 220 { 221 } 222 } 223 } 224 return byteArray; 225 } 226 227 private static void throwTorqueException(Exception e) 228 throws TorqueException 229 { 230 if (e instanceof TorqueException) 231 { 232 throw (TorqueException)e; 233 } 234 else 235 { 236 throw new TorqueException(e); 237 } 238 } 239 240 /*** 241 * Sets up a Schema for a table. This schema is then normally 242 * used as the argument for initTableColumns(). 243 * 244 * @param tableName The name of the table. 245 * @return A Schema. 246 */ 247 public static Schema initTableSchema(String tableName) 248 { 249 return initTableSchema(tableName, Torque.getDefaultDB()); 250 } 251 252 /*** 253 * Sets up a Schema for a table. This schema is then normally 254 * used as the argument for initTableColumns 255 * 256 * @param tableName The propery name for the database in the 257 * configuration file. 258 * @param dbName The name of the database. 259 * @return A Schema. 260 */ 261 public static Schema initTableSchema(String tableName, String dbName) 262 { 263 Schema schema = null; 264 Connection con = null; 265 266 try 267 { 268 con = Torque.getConnection(dbName); 269 schema = new Schema().schema(con, tableName); 270 } 271 catch (Exception e) 272 { 273 log.error(e); 274 throw new Error("Error in BasePeer.initTableSchema(" 275 + tableName 276 + "): " 277 + e.getMessage()); 278 } 279 finally 280 { 281 Torque.closeConnection(con); 282 } 283 return schema; 284 } 285 286 /*** 287 * Creates a Column array for a table based on its Schema. 288 * 289 * @param schema A Schema object. 290 * @return A Column[]. 291 */ 292 public static Column[] initTableColumns(Schema schema) 293 { 294 Column[] columns = null; 295 try 296 { 297 int numberOfColumns = schema.numberOfColumns(); 298 columns = new Column[numberOfColumns]; 299 for (int i = 0; i < numberOfColumns; i++) 300 { 301 columns[i] = schema.column(i + 1); 302 } 303 } 304 catch (Exception e) 305 { 306 log.error(e); 307 throw new Error( 308 "Error in BasePeer.initTableColumns(): " + e.getMessage()); 309 } 310 return columns; 311 } 312 313 /*** 314 * Convenience method to create a String array of column names. 315 * 316 * @param columns A Column[]. 317 * @return A String[]. 318 */ 319 public static String[] initColumnNames(Column[] columns) 320 { 321 String[] columnNames = null; 322 columnNames = new String[columns.length]; 323 for (int i = 0; i < columns.length; i++) 324 { 325 columnNames[i] = columns[i].name().toUpperCase(); 326 } 327 return columnNames; 328 } 329 330 /*** 331 * Convenience method to create a String array of criteria keys. 332 * 333 * @param tableName Name of table. 334 * @param columnNames A String[]. 335 * @return A String[]. 336 */ 337 public static String[] initCriteriaKeys( 338 String tableName, 339 String[] columnNames) 340 { 341 String[] keys = new String[columnNames.length]; 342 for (int i = 0; i < columnNames.length; i++) 343 { 344 keys[i] = tableName + "." + columnNames[i].toUpperCase(); 345 } 346 return keys; 347 } 348 349 /*** 350 * Convenience method that uses straight JDBC to delete multiple 351 * rows. Village throws an Exception when multiple rows are 352 * deleted. 353 * 354 * @param con A Connection. 355 * @param table The table to delete records from. 356 * @param column The column in the where clause. 357 * @param value The value of the column. 358 * @throws TorqueException Any exceptions caught during processing will be 359 * rethrown wrapped into a TorqueException. 360 */ 361 public static void deleteAll( 362 Connection con, 363 String table, 364 String column, 365 int value) 366 throws TorqueException 367 { 368 Statement statement = null; 369 try 370 { 371 statement = con.createStatement(); 372 373 StringBuffer query = new StringBuffer(); 374 query.append("DELETE FROM ") 375 .append(table) 376 .append(" WHERE ") 377 .append(column) 378 .append(" = ") 379 .append(value); 380 381 statement.executeUpdate(query.toString()); 382 } 383 catch (SQLException e) 384 { 385 throw new TorqueException(e); 386 } 387 finally 388 { 389 if (statement != null) 390 { 391 try 392 { 393 statement.close(); 394 } 395 catch (SQLException ignored) 396 { 397 } 398 } 399 } 400 } 401 402 /*** 403 * Convenience method that uses straight JDBC to delete multiple 404 * rows. Village throws an Exception when multiple rows are 405 * deleted. This method attempts to get the default database from 406 * the pool. 407 * 408 * @param table The table to delete records from. 409 * @param column The column in the where clause. 410 * @param value The value of the column. 411 * @throws TorqueException Any exceptions caught during processing will be 412 * rethrown wrapped into a TorqueException. 413 */ 414 public static void deleteAll(String table, String column, int value) 415 throws TorqueException 416 { 417 Connection con = null; 418 try 419 { 420 // Get a connection to the db. 421 con = Torque.getConnection("default"); 422 deleteAll(con, table, column, value); 423 } 424 finally 425 { 426 Torque.closeConnection(con); 427 } 428 } 429 430 /*** 431 * Method to perform deletes based on values and keys in a 432 * Criteria. 433 * 434 * @param criteria The criteria to use. 435 * @throws TorqueException Any exceptions caught during processing will be 436 * rethrown wrapped into a TorqueException. 437 */ 438 public static void doDelete(Criteria criteria) throws TorqueException 439 { 440 Connection con = null; 441 try 442 { 443 con = Transaction.beginOptional( 444 criteria.getDbName(), 445 criteria.isUseTransaction()); 446 doDelete(criteria, con); 447 Transaction.commit(con); 448 } 449 catch (TorqueException e) 450 { 451 Transaction.safeRollback(con); 452 throw e; 453 } 454 } 455 456 /*** 457 * Method to perform deletes based on values and keys in a Criteria. 458 * 459 * @param criteria The criteria to use. 460 * @param con A Connection. 461 * @throws TorqueException Any exceptions caught during processing will be 462 * rethrown wrapped into a TorqueException. 463 */ 464 public static void doDelete(Criteria criteria, Connection con) 465 throws TorqueException 466 { 467 DB db = Torque.getDB(criteria.getDbName()); 468 DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); 469 470 // Set up a list of required tables and add extra entries to 471 // criteria if directed to delete all related records. 472 // StringStack.add() only adds element if it is unique. 473 HashSet tables = new HashSet(); 474 Iterator it = criteria.keySet().iterator(); 475 while (it.hasNext()) 476 { 477 String key = (String) it.next(); 478 Criteria.Criterion c = criteria.getCriterion(key); 479 List tableNames = c.getAllTables(); 480 for (int i = 0; i < tableNames.size(); i++) 481 { 482 String name = (String) tableNames.get(i); 483 String tableName2 = criteria.getTableForAlias(name); 484 if (tableName2 != null) 485 { 486 tables.add(new StringBuffer( 487 name.length() + tableName2.length() + 1) 488 .append(tableName2) 489 .append(' ') 490 .append(name) 491 .toString()); 492 } 493 else 494 { 495 tables.add(name); 496 } 497 } 498 499 if (criteria.isCascade()) 500 { 501 // This steps thru all the columns in the database. 502 TableMap[] tableMaps = dbMap.getTables(); 503 for (int i = 0; i < tableMaps.length; i++) 504 { 505 ColumnMap[] columnMaps = tableMaps[i].getColumns(); 506 for (int j = 0; j < columnMaps.length; j++) 507 { 508 // Only delete rows where the foreign key is 509 // also a primary key. Other rows need 510 // updating, but that is not implemented. 511 if (columnMaps[j].isForeignKey() 512 && columnMaps[j].isPrimaryKey() 513 && key.equals(columnMaps[j].getRelatedName())) 514 { 515 tables.add(tableMaps[i].getName()); 516 criteria.add(columnMaps[j].getFullyQualifiedName(), 517 criteria.getValue(key)); 518 } 519 } 520 } 521 } 522 } 523 Iterator tabIt = tables.iterator(); 524 while (tabIt.hasNext()) 525 { 526 String tab = (String) tabIt.next(); 527 KeyDef kd = new KeyDef(); 528 HashSet whereClause = new HashSet(); 529 530 ColumnMap[] columnMaps = dbMap.getTable(tab).getColumns(); 531 for (int j = 0; j < columnMaps.length; j++) 532 { 533 ColumnMap colMap = columnMaps[j]; 534 if (colMap.isPrimaryKey()) 535 { 536 kd.addAttrib(colMap.getColumnName()); 537 } 538 String key = new StringBuffer(colMap.getTableName()) 539 .append('.') 540 .append(colMap.getColumnName()) 541 .toString(); 542 if (criteria.containsKey(key)) 543 { 544 if (criteria.getComparison(key).equals(Criteria.CUSTOM)) 545 { 546 whereClause.add(criteria.getString(key)); 547 } 548 else 549 { 550 whereClause.add(SqlExpression.build( 551 colMap.getColumnName(), 552 criteria.getValue(key), 553 criteria.getComparison(key), 554 criteria.isIgnoreCase(), 555 db)); 556 } 557 } 558 } 559 560 // Execute the statement. 561 TableDataSet tds = null; 562 try 563 { 564 tds = new TableDataSet(con, tab, kd); 565 String sqlSnippet = StringUtils.join(whereClause.iterator(), " AND "); 566 567 if (log.isDebugEnabled()) 568 { 569 log.debug("BasePeer.doDelete: whereClause=" + sqlSnippet); 570 } 571 572 tds.where(sqlSnippet); 573 tds.fetchRecords(); 574 if (tds.size() > 1 && criteria.isSingleRecord()) 575 { 576 handleMultipleRecords(tds); 577 } 578 for (int j = 0; j < tds.size(); j++) 579 { 580 Record rec = tds.getRecord(j); 581 rec.markToBeDeleted(); 582 rec.save(); 583 } 584 } 585 catch (Exception e) 586 { 587 throwTorqueException(e); 588 } 589 finally 590 { 591 if (tds != null) 592 { 593 try 594 { 595 tds.close(); 596 } 597 catch (Exception ignored) 598 { 599 } 600 } 601 } 602 } 603 } 604 605 /*** 606 * Method to perform inserts based on values and keys in a 607 * Criteria. 608 * <p> 609 * If the primary key is auto incremented the data in Criteria 610 * will be inserted and the auto increment value will be returned. 611 * <p> 612 * If the primary key is included in Criteria then that value will 613 * be used to insert the row. 614 * <p> 615 * If no primary key is included in Criteria then we will try to 616 * figure out the primary key from the database map and insert the 617 * row with the next available id using util.db.IDBroker. 618 * <p> 619 * If no primary key is defined for the table the values will be 620 * inserted as specified in Criteria and -1 will be returned. 621 * 622 * @param criteria Object containing values to insert. 623 * @return An Object which is the id of the row that was inserted 624 * (if the table has a primary key) or null (if the table does not 625 * have a primary key). 626 * @throws TorqueException Any exceptions caught during processing will be 627 * rethrown wrapped into a TorqueException. 628 */ 629 public static ObjectKey doInsert(Criteria criteria) throws TorqueException 630 { 631 Connection con = null; 632 ObjectKey id = null; 633 634 try 635 { 636 con = Transaction.beginOptional( 637 criteria.getDbName(), 638 criteria.isUseTransaction()); 639 id = doInsert(criteria, con); 640 Transaction.commit(con); 641 } 642 catch (TorqueException e) 643 { 644 Transaction.safeRollback(con); 645 throw e; 646 } 647 648 return id; 649 } 650 651 /*** 652 * Method to perform inserts based on values and keys in a 653 * Criteria. 654 * <p> 655 * If the primary key is auto incremented the data in Criteria 656 * will be inserted and the auto increment value will be returned. 657 * <p> 658 * If the primary key is included in Criteria then that value will 659 * be used to insert the row. 660 * <p> 661 * If no primary key is included in Criteria then we will try to 662 * figure out the primary key from the database map and insert the 663 * row with the next available id using util.db.IDBroker. 664 * <p> 665 * If no primary key is defined for the table the values will be 666 * inserted as specified in Criteria and null will be returned. 667 * 668 * @param criteria Object containing values to insert. 669 * @param con A Connection. 670 * @return An Object which is the id of the row that was inserted 671 * (if the table has a primary key) or null (if the table does not 672 * have a primary key). 673 * @throws TorqueException Any exceptions caught during processing will be 674 * rethrown wrapped into a TorqueException. 675 */ 676 public static ObjectKey doInsert(Criteria criteria, Connection con) 677 throws TorqueException 678 { 679 SimpleKey id = null; 680 681 // Get the table name and method for determining the primary 682 // key value. 683 String tableName = null; 684 Iterator keys = criteria.keySet().iterator(); 685 if (keys.hasNext()) 686 { 687 tableName = criteria.getTableName((String) keys.next()); 688 } 689 else 690 { 691 throw new TorqueException("Database insert attempted without " 692 + "anything specified to insert"); 693 } 694 695 DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); 696 TableMap tableMap = dbMap.getTable(tableName); 697 Object keyInfo = tableMap.getPrimaryKeyMethodInfo(); 698 IdGenerator keyGen = tableMap.getIdGenerator(); 699 700 ColumnMap pk = getPrimaryKey(criteria); 701 702 // pk will be null if there is no primary key defined for the table 703 // we're inserting into. 704 if (pk != null && !criteria.containsKey(pk.getFullyQualifiedName())) 705 { 706 if (keyGen == null) 707 { 708 throw new TorqueException( 709 "IdGenerator for table '" + tableName + "' is null"); 710 } 711 // If the keyMethod is SEQUENCE or IDBROKERTABLE, get the id 712 // before the insert. 713 714 if (keyGen.isPriorToInsert()) 715 { 716 try 717 { 718 if (pk.getType() instanceof Number) 719 { 720 id = new NumberKey( 721 keyGen.getIdAsBigDecimal(con, keyInfo)); 722 } 723 else 724 { 725 id = new StringKey(keyGen.getIdAsString(con, keyInfo)); 726 } 727 } 728 catch (Exception e) 729 { 730 throwTorqueException(e); 731 } 732 criteria.add(pk.getFullyQualifiedName(), id); 733 } 734 } 735 736 // Use Village to perform the insert. 737 TableDataSet tds = null; 738 try 739 { 740 tds = new TableDataSet(con, tableName); 741 Record rec = tds.addRecord(); 742 BasePeer.insertOrUpdateRecord(rec, tableName, criteria); 743 } 744 catch (Exception e) 745 { 746 throwTorqueException(e); 747 } 748 finally 749 { 750 if (tds != null) 751 { 752 try 753 { 754 tds.close(); 755 } 756 catch (Exception e) 757 { 758 throwTorqueException(e); 759 } 760 } 761 } 762 763 // If the primary key column is auto-incremented, get the id 764 // now. 765 if (pk != null && keyGen != null && keyGen.isPostInsert()) 766 { 767 try 768 { 769 if (pk.getType() instanceof Number) 770 { 771 id = new NumberKey(keyGen.getIdAsBigDecimal(con, keyInfo)); 772 } 773 else 774 { 775 id = new StringKey(keyGen.getIdAsString(con, keyInfo)); 776 } 777 } 778 catch (Exception e) 779 { 780 throwTorqueException(e); 781 } 782 } 783 784 return id; 785 } 786 787 /*** 788 * Grouping of code used in both doInsert() and doUpdate() 789 * methods. Sets up a Record for saving. 790 * 791 * @param rec A Record. 792 * @param tableName Name of table. 793 * @param criteria A Criteria. 794 * @throws TorqueException Any exceptions caught during processing will be 795 * rethrown wrapped into a TorqueException. 796 */ 797 private static void insertOrUpdateRecord( 798 Record rec, 799 String tableName, 800 Criteria criteria) 801 throws TorqueException 802 { 803 DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); 804 805 ColumnMap[] columnMaps = dbMap.getTable(tableName).getColumns(); 806 boolean shouldSave = false; 807 for (int j = 0; j < columnMaps.length; j++) 808 { 809 ColumnMap colMap = columnMaps[j]; 810 String key = new StringBuffer(colMap.getTableName()) 811 .append('.') 812 .append(colMap.getColumnName()) 813 .toString(); 814 if (criteria.containsKey(key)) 815 { 816 // A village Record.setValue( String, Object ) would 817 // be nice here. 818 Object obj = criteria.getValue(key); 819 if (obj instanceof SimpleKey) 820 { 821 obj = ((SimpleKey) obj).getValue(); 822 } 823 try 824 { 825 if (obj == null) 826 { 827 rec.setValueNull(colMap.getColumnName()); 828 } 829 else if (obj instanceof String) 830 { 831 rec.setValue(colMap.getColumnName(), (String) obj); 832 } 833 else if (obj instanceof Integer) 834 { 835 rec.setValue(colMap.getColumnName(), 836 criteria.getInt(key)); 837 } 838 else if (obj instanceof BigDecimal) 839 { 840 rec.setValue(colMap.getColumnName(), (BigDecimal) obj); 841 } 842 else if (obj instanceof Boolean) 843 { 844 rec.setValue(colMap.getColumnName(), 845 criteria.getBoolean(key) ? 1 : 0); 846 } 847 else if (obj instanceof java.util.Date) 848 { 849 rec.setValue(colMap.getColumnName(), 850 (java.util.Date) obj); 851 } 852 else if (obj instanceof Float) 853 { 854 rec.setValue(colMap.getColumnName(), 855 criteria.getFloat(key)); 856 } 857 else if (obj instanceof Double) 858 { 859 rec.setValue(colMap.getColumnName(), 860 criteria.getDouble(key)); 861 } 862 else if (obj instanceof Byte) 863 { 864 rec.setValue(colMap.getColumnName(), 865 ((Byte) obj).byteValue()); 866 } 867 else if (obj instanceof Long) 868 { 869 rec.setValue(colMap.getColumnName(), 870 criteria.getLong(key)); 871 } 872 else if (obj instanceof Short) 873 { 874 rec.setValue(colMap.getColumnName(), 875 ((Short) obj).shortValue()); 876 } 877 else if (obj instanceof Hashtable) 878 { 879 rec.setValue(colMap.getColumnName(), 880 hashtableToByteArray((Hashtable) obj)); 881 } 882 else if (obj instanceof byte[]) 883 { 884 rec.setValue(colMap.getColumnName(), (byte[]) obj); 885 } 886 } 887 catch (Exception e) 888 { 889 throwTorqueException(e); 890 } 891 shouldSave = true; 892 } 893 } 894 895 if (shouldSave) 896 { 897 try 898 { 899 rec.save(); 900 } 901 catch (Exception e) 902 { 903 throwTorqueException(e); 904 } 905 } 906 else 907 { 908 throw new TorqueException("No changes to save"); 909 } 910 } 911 912 /*** 913 * Method to create an SQL query for display only based on values in a 914 * Criteria. 915 * 916 * @param criteria A Criteria. 917 * @return the SQL query for display 918 * @exception TorqueException Trouble creating the query string. 919 */ 920 static String createQueryDisplayString(Criteria criteria) 921 throws TorqueException 922 { 923 return createQuery(criteria).toString(); 924 } 925 926 /*** 927 * Build Oracle-style query with limit or offset. 928 * If the original SQL is in variable: query then the requlting 929 * SQL looks like this: 930 * <pre> 931 * SELECT B.* FROM ( 932 * SELECT A.*, rownum as TORQUE$ROWNUM FROM ( 933 * query 934 * ) A 935 * ) B WHERE B.TORQUE$ROWNUM > offset AND B.TORQUE$ROWNUM 936 * <= offset + limit 937 * </pre> 938 * 939 * @param query the query 940 * @param limit 941 * @param offset 942 * @return oracle-style query 943 */ 944 private static String createOracleLimitOffsetQuery(Query query, 945 int limit, int offset) 946 { 947 StringBuffer buf = new StringBuffer(); 948 buf.append("SELECT B.* FROM ( "); 949 buf.append("SELECT A.*, rownum AS TORQUE$ROWNUM FROM ( "); 950 951 buf.append(query.toString()); 952 buf.append(" ) A "); 953 buf.append(" ) B WHERE "); 954 955 if (offset > 0) 956 { 957 buf.append(" B.TORQUE$ROWNUM > "); 958 buf.append(offset); 959 if (limit > 0) 960 { 961 buf.append(" AND B.TORQUE$ROWNUM <= "); 962 buf.append(offset + limit); 963 } 964 } 965 else 966 { 967 buf.append(" B.TORQUE$ROWNUM <= "); 968 buf.append(limit); 969 } 970 return buf.toString(); 971 } 972 973 /*** 974 * Method to create an SQL query for actual execution based on values in a 975 * Criteria. 976 * 977 * @param criteria A Criteria. 978 * @return the SQL query for actual execution 979 * @exception TorqueException Trouble creating the query string. 980 */ 981 public static String createQueryString(Criteria criteria) 982 throws TorqueException 983 { 984 Query query = createQuery(criteria); 985 DB db = Torque.getDB(criteria.getDbName()); 986 987 // Limit the number of rows returned. 988 int limit = criteria.getLimit(); 989 int offset = criteria.getOffset(); 990 991 String sql; 992 if ((limit > 0 || offset > 0) 993 && db.getLimitStyle() == DB.LIMIT_STYLE_ORACLE) 994 { 995 sql = createOracleLimitOffsetQuery(query, limit, offset); 996 criteria.setLimit(-1); 997 criteria.setOffset(0); 998 } 999 else 1000 { 1001 if (offset > 0 && db.supportsNativeOffset()) 1002 { 1003 // Now set the criteria's limit and offset to return the 1004 // full resultset since the results are limited on the 1005 // server. 1006 criteria.setLimit(-1); 1007 criteria.setOffset(0); 1008 } 1009 else if (limit > 0 && db.supportsNativeLimit()) 1010 { 1011 // Now set the criteria's limit to return the full 1012 // resultset since the results are limited on the server. 1013 criteria.setLimit(-1); 1014 } 1015 sql = query.toString(); 1016 } 1017 if (log.isDebugEnabled()) 1018 { 1019 log.debug(sql); 1020 } 1021 return sql; 1022 } 1023 1024 /*** 1025 * Method to create an SQL query based on values in a Criteria. Note that 1026 * final manipulation of the limit and offset are performed when the query 1027 * is actually executed. 1028 * 1029 * @param criteria A Criteria. 1030 * @return the sql query 1031 * @exception TorqueException Trouble creating the query string. 1032 */ 1033 static Query createQuery(Criteria criteria) 1034 throws TorqueException 1035 { 1036 Query query = new Query(); 1037 DB db = Torque.getDB(criteria.getDbName()); 1038 DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); 1039 1040 UniqueList selectModifiers = query.getSelectModifiers(); 1041 UniqueList selectClause = query.getSelectClause(); 1042 UniqueList fromClause = query.getFromClause(); 1043 UniqueList whereClause = query.getWhereClause(); 1044 UniqueList orderByClause = query.getOrderByClause(); 1045 UniqueList groupByClause = query.getGroupByClause(); 1046 1047 UniqueList orderBy = criteria.getOrderByColumns(); 1048 UniqueList groupBy = criteria.getGroupByColumns(); 1049 UniqueList select = criteria.getSelectColumns(); 1050 Hashtable aliases = criteria.getAsColumns(); 1051 UniqueList modifiers = criteria.getSelectModifiers(); 1052 1053 for (int i = 0; i < modifiers.size(); i++) 1054 { 1055 selectModifiers.add(modifiers.get(i)); 1056 } 1057 1058 for (int i = 0; i < select.size(); i++) 1059 { 1060 String columnName = (String) select.get(i); 1061 if (columnName.indexOf('.') == -1 && columnName.indexOf('*') == -1) 1062 { 1063 throwMalformedColumnNameException("select", columnName); 1064 } 1065 String tableName = null; 1066 selectClause.add(columnName); 1067 int parenPos = columnName.indexOf('('); 1068 if (parenPos == -1) 1069 { 1070 tableName = columnName.substring(0, columnName.indexOf('.')); 1071 } 1072 else if (columnName.indexOf('.') > -1) 1073 { 1074 tableName = 1075 columnName.substring(parenPos + 1, columnName.indexOf('.')); 1076 // functions may contain qualifiers so only take the last 1077 // word as the table name. 1078 int lastSpace = tableName.lastIndexOf(' '); 1079 if (lastSpace != -1) 1080 { 1081 tableName = tableName.substring(lastSpace + 1); 1082 } 1083 } 1084 String tableName2 = criteria.getTableForAlias(tableName); 1085 if (tableName2 != null) 1086 { 1087 fromClause.add(new StringBuffer( 1088 tableName.length() + tableName2.length() + 1) 1089 .append(tableName2) 1090 .append(' ') 1091 .append(tableName) 1092 .toString()); 1093 } 1094 else 1095 { 1096 fromClause.add(tableName); 1097 } 1098 } 1099 1100 Iterator it = aliases.keySet().iterator(); 1101 while (it.hasNext()) 1102 { 1103 String key = (String) it.next(); 1104 selectClause.add((String) aliases.get(key) + " AS " + key); 1105 } 1106 1107 Iterator critKeys = criteria.keySet().iterator(); 1108 while (critKeys.hasNext()) 1109 { 1110 String key = (String) critKeys.next(); 1111 Criteria.Criterion criterion = criteria.getCriterion(key); 1112 Criteria.Criterion[] someCriteria = 1113 criterion.getAttachedCriterion(); 1114 String table = null; 1115 for (int i = 0; i < someCriteria.length; i++) 1116 { 1117 String tableName = someCriteria[i].getTable(); 1118 table = criteria.getTableForAlias(tableName); 1119 if (table != null) 1120 { 1121 fromClause.add(new StringBuffer( 1122 tableName.length() + table.length() + 1) 1123 .append(table) 1124 .append(' ') 1125 .append(tableName) 1126 .toString()); 1127 } 1128 else 1129 { 1130 fromClause.add(tableName); 1131 table = tableName; 1132 } 1133 1134 boolean ignorCase = ((criteria.isIgnoreCase() 1135 || someCriteria[i].isIgnoreCase()) 1136 && (dbMap 1137 .getTable(table) 1138 .getColumn(someCriteria[i].getColumn()) 1139 .getType() 1140 instanceof String)); 1141 1142 someCriteria[i].setIgnoreCase(ignorCase); 1143 } 1144 1145 criterion.setDB(db); 1146 whereClause.add(criterion.toString()); 1147 } 1148 1149 List join = criteria.getJoinL(); 1150 if (join != null) 1151 { 1152 for (int i = 0; i < join.size(); i++) 1153 { 1154 String join1 = (String) join.get(i); 1155 String join2 = (String) criteria.getJoinR().get(i); 1156 if (join1.indexOf('.') == -1) 1157 { 1158 throwMalformedColumnNameException("join", join1); 1159 } 1160 if (join2.indexOf('.') == -1) 1161 { 1162 throwMalformedColumnNameException("join", join2); 1163 } 1164 1165 String tableName = join1.substring(0, join1.indexOf('.')); 1166 String table = criteria.getTableForAlias(tableName); 1167 if (table != null) 1168 { 1169 fromClause.add(new StringBuffer( 1170 tableName.length() + table.length() + 1) 1171 .append(table) 1172 .append(' ') 1173 .append(tableName) 1174 .toString()); 1175 } 1176 else 1177 { 1178 fromClause.add(tableName); 1179 } 1180 1181 int dot = join2.indexOf('.'); 1182 tableName = join2.substring(0, dot); 1183 table = criteria.getTableForAlias(tableName); 1184 if (table != null) 1185 { 1186 fromClause.add(new StringBuffer( 1187 tableName.length() + table.length() + 1) 1188 .append(table) 1189 .append(' ') 1190 .append(tableName) 1191 .toString()); 1192 } 1193 else 1194 { 1195 fromClause.add(tableName); 1196 table = tableName; 1197 } 1198 1199 boolean ignorCase = (criteria.isIgnoreCase() 1200 && (dbMap 1201 .getTable(table) 1202 .getColumn(join2.substring(dot + 1, join2.length())) 1203 .getType() 1204 instanceof String)); 1205 1206 whereClause.add( 1207 SqlExpression.buildInnerJoin(join1, join2, ignorCase, db)); 1208 } 1209 } 1210 1211 // need to allow for multiple group bys 1212 if (groupBy != null && groupBy.size() > 0) 1213 { 1214 for (int i = 0; i < groupBy.size(); i++) 1215 { 1216 String groupByColumn = (String) groupBy.get(i); 1217 if (groupByColumn.indexOf('.') == -1) 1218 { 1219 throwMalformedColumnNameException("group by", 1220 groupByColumn); 1221 } 1222 groupByClause.add(groupByColumn); 1223 } 1224 } 1225 1226 Criteria.Criterion having = criteria.getHaving(); 1227 if (having != null) 1228 { 1229 //String groupByString = null; 1230 query.setHaving(having.toString()); 1231 } 1232 1233 if (orderBy != null && orderBy.size() > 0) 1234 { 1235 // Check for each String/Character column and apply 1236 // toUpperCase(). 1237 for (int i = 0; i < orderBy.size(); i++) 1238 { 1239 String orderByColumn = (String) orderBy.get(i); 1240 if (orderByColumn.indexOf('.') == -1) 1241 { 1242 throwMalformedColumnNameException("order by", 1243 orderByColumn); 1244 } 1245 String tableName = 1246 orderByColumn.substring(0, orderByColumn.indexOf('.')); 1247 String table = criteria.getTableForAlias(tableName); 1248 if (table == null) 1249 { 1250 table = tableName; 1251 } 1252 1253 // See if there's a space (between the column list and sort 1254 // order in ORDER BY table.column DESC). 1255 int spacePos = orderByColumn.indexOf(' '); 1256 String columnName; 1257 if (spacePos == -1) 1258 { 1259 columnName = 1260 orderByColumn.substring(orderByColumn.indexOf('.') + 1); 1261 } 1262 else 1263 { 1264 columnName = orderByColumn.substring( 1265 orderByColumn.indexOf('.') + 1, spacePos); 1266 } 1267 ColumnMap column = dbMap.getTable(table).getColumn(columnName); 1268 if (column.getType() instanceof String) 1269 { 1270 if (spacePos == -1) 1271 { 1272 orderByClause.add( 1273 db.ignoreCaseInOrderBy(orderByColumn)); 1274 } 1275 else 1276 { 1277 orderByClause.add(db.ignoreCaseInOrderBy( 1278 orderByColumn.substring(0, spacePos)) 1279 + orderByColumn.substring(spacePos)); 1280 } 1281 selectClause.add( 1282 db.ignoreCaseInOrderBy(table + '.' + columnName)); 1283 } 1284 else 1285 { 1286 orderByClause.add(orderByColumn); 1287 } 1288 } 1289 } 1290 1291 // Limit the number of rows returned. 1292 int limit = criteria.getLimit(); 1293 int offset = criteria.getOffset(); 1294 String limitString = null; 1295 if (offset > 0 && db.supportsNativeOffset()) 1296 { 1297 switch (db.getLimitStyle()) 1298 { 1299 case DB.LIMIT_STYLE_MYSQL : 1300 limitString = new StringBuffer() 1301 .append(offset) 1302 .append(", ") 1303 .append(limit) 1304 .toString(); 1305 break; 1306 case DB.LIMIT_STYLE_POSTGRES : 1307 limitString = new StringBuffer() 1308 .append(limit) 1309 .append(" offset ") 1310 .append(offset) 1311 .toString(); 1312 break; 1313 } 1314 1315 // The following is now done in createQueryString() to enable this 1316 // method to be used as part of Criteria.toString() without altering 1317 // the criteria itself. The commented code is retained here to 1318 // make it easier to understand how the criteria is built into a 1319 // query. 1320 1321 // Now set the criteria's limit and offset to return the 1322 // full resultset since the results are limited on the 1323 // server. 1324 //criteria.setLimit(-1); 1325 //criteria.setOffset(0); 1326 } 1327 else if (limit > 0 && db.supportsNativeLimit() 1328 && db.getLimitStyle() != DB.LIMIT_STYLE_ORACLE) 1329 { 1330 limitString = String.valueOf(limit); 1331 1332 // The following is now done in createQueryString() to enable this 1333 // method to be used as part of Criteria.toString() without altering 1334 // the criteria itself. The commented code is retained here to 1335 // make it easier to understand how the criteria is built into a 1336 // query. 1337 1338 // Now set the criteria's limit to return the full 1339 // resultset since the results are limited on the server. 1340 //criteria.setLimit(-1); 1341 } 1342 1343 if (limitString != null) 1344 { 1345 query.setLimit(limitString); 1346 } 1347 return query; 1348 } 1349 1350 /*** 1351 * Returns all results. 1352 * 1353 * @param criteria A Criteria. 1354 * @return List of Record objects. 1355 * @throws TorqueException Any exceptions caught during processing will be 1356 * rethrown wrapped into a TorqueException. 1357 */ 1358 public static List doSelect(Criteria criteria) throws TorqueException 1359 { 1360 Connection con = null; 1361 List results = null; 1362 1363 try 1364 { 1365 con = Transaction.beginOptional( 1366 criteria.getDbName(), 1367 criteria.isUseTransaction()); 1368 results = doSelect(criteria, con); 1369 Transaction.commit(con); 1370 } 1371 catch (Exception e) 1372 { 1373 Transaction.safeRollback(con); 1374 throwTorqueException(e); 1375 } 1376 return results; 1377 } 1378 1379 /*** 1380 * Returns all results. 1381 * 1382 * @param criteria A Criteria. 1383 * @param con A Connection. 1384 * @return List of Record objects. 1385 * @throws TorqueException Any exceptions caught during processing will be 1386 * rethrown wrapped into a TorqueException. 1387 */ 1388 public static List doSelect(Criteria criteria, Connection con) 1389 throws TorqueException 1390 { 1391 return executeQuery( 1392 createQueryString(criteria), 1393 criteria.getOffset(), 1394 criteria.getLimit(), 1395 criteria.isSingleRecord(), 1396 con); 1397 } 1398 1399 /*** 1400 * Utility method which executes a given sql statement. This 1401 * method should be used for select statements only. Use 1402 * executeStatement for update, insert, and delete operations. 1403 * 1404 * @param queryString A String with the sql statement to execute. 1405 * @return List of Record objects. 1406 * @throws TorqueException Any exceptions caught during processing will be 1407 * rethrown wrapped into a TorqueException. 1408 */ 1409 public static List executeQuery(String queryString) throws TorqueException 1410 { 1411 return executeQuery(queryString, Torque.getDefaultDB(), false); 1412 } 1413 1414 /*** 1415 * Utility method which executes a given sql statement. This 1416 * method should be used for select statements only. Use 1417 * executeStatement for update, insert, and delete operations. 1418 * 1419 * @param queryString A String with the sql statement to execute. 1420 * @param dbName The database to connect to. 1421 * @return List of Record objects. 1422 * @throws TorqueException Any exceptions caught during processing will be 1423 * rethrown wrapped into a TorqueException. 1424 */ 1425 public static List executeQuery(String queryString, String dbName) 1426 throws TorqueException 1427 { 1428 return executeQuery(queryString, dbName, false); 1429 } 1430 1431 /*** 1432 * Method for performing a SELECT. Returns all results. 1433 * 1434 * @param queryString A String with the sql statement to execute. 1435 * @param dbName The database to connect to. 1436 * @param singleRecord Whether or not we want to select only a 1437 * single record. 1438 * @return List of Record objects. 1439 * @throws TorqueException Any exceptions caught during processing will be 1440 * rethrown wrapped into a TorqueException. 1441 */ 1442 public static List executeQuery( 1443 String queryString, 1444 String dbName, 1445 boolean singleRecord) 1446 throws TorqueException 1447 { 1448 return executeQuery(queryString, 0, -1, dbName, singleRecord); 1449 } 1450 1451 /*** 1452 * Method for performing a SELECT. Returns all results. 1453 * 1454 * @param queryString A String with the sql statement to execute. 1455 * @param singleRecord Whether or not we want to select only a 1456 * single record. 1457 * @param con A Connection. 1458 * @return List of Record objects. 1459 * @throws TorqueException Any exceptions caught during processing will be 1460 * rethrown wrapped into a TorqueException. 1461 */ 1462 public static List executeQuery( 1463 String queryString, 1464 boolean singleRecord, 1465 Connection con) 1466 throws TorqueException 1467 { 1468 return executeQuery(queryString, 0, -1, singleRecord, con); 1469 } 1470 1471 /*** 1472 * Method for performing a SELECT. 1473 * 1474 * @param queryString A String with the sql statement to execute. 1475 * @param start The first row to return. 1476 * @param numberOfResults The number of rows to return. 1477 * @param dbName The database to connect to. 1478 * @param singleRecord Whether or not we want to select only a 1479 * single record. 1480 * @return List of Record objects. 1481 * @throws TorqueException Any exceptions caught during processing will be 1482 * rethrown wrapped into a TorqueException. 1483 */ 1484 public static List executeQuery( 1485 String queryString, 1486 int start, 1487 int numberOfResults, 1488 String dbName, 1489 boolean singleRecord) 1490 throws TorqueException 1491 { 1492 Connection db = null; 1493 List results = null; 1494 try 1495 { 1496 db = Torque.getConnection(dbName); 1497 // execute the query 1498 results = executeQuery( 1499 queryString, 1500 start, 1501 numberOfResults, 1502 singleRecord, 1503 db); 1504 } 1505 finally 1506 { 1507 Torque.closeConnection(db); 1508 } 1509 return results; 1510 } 1511 1512 /*** 1513 * Method for performing a SELECT. Returns all results. 1514 * 1515 * @param queryString A String with the sql statement to execute. 1516 * @param start The first row to return. 1517 * @param numberOfResults The number of rows to return. 1518 * @param singleRecord Whether or not we want to select only a 1519 * single record. 1520 * @param con A Connection. 1521 * @return List of Record objects. 1522 * @throws TorqueException Any exceptions caught during processing will be 1523 * rethrown wrapped into a TorqueException. 1524 */ 1525 public static List executeQuery( 1526 String queryString, 1527 int start, 1528 int numberOfResults, 1529 boolean singleRecord, 1530 Connection con) 1531 throws TorqueException 1532 { 1533 QueryDataSet qds = null; 1534 List results = Collections.EMPTY_LIST; 1535 try 1536 { 1537 // execute the query 1538 long startTime = System.currentTimeMillis(); 1539 qds = new QueryDataSet(con, queryString); 1540 if (log.isDebugEnabled()) 1541 { 1542 log.debug("Elapsed time=" 1543 + (System.currentTimeMillis() - startTime) + " ms"); 1544 } 1545 results = getSelectResults( 1546 qds, start, numberOfResults, singleRecord); 1547 } 1548 catch (Exception e) 1549 { 1550 throwTorqueException(e); 1551 } 1552 finally 1553 { 1554 if (qds != null) 1555 { 1556 try 1557 { 1558 qds.close(); 1559 } 1560 catch (Exception ignored) 1561 { 1562 } 1563 } 1564 } 1565 return results; 1566 } 1567 1568 /*** 1569 * Returns all records in a QueryDataSet as a List of Record 1570 * objects. Used for functionality like util.LargeSelect. 1571 * 1572 * @see #getSelectResults(QueryDataSet, int, int, boolean) 1573 * @param qds the QueryDataSet 1574 * @return a List of Record objects 1575 * @throws TorqueException Any exceptions caught during processing will be 1576 * rethrown wrapped into a TorqueException. 1577 */ 1578 public static List getSelectResults(QueryDataSet qds) 1579 throws TorqueException 1580 { 1581 return getSelectResults(qds, 0, -1, false); 1582 } 1583 1584 /*** 1585 * Returns all records in a QueryDataSet as a List of Record 1586 * objects. Used for functionality like util.LargeSelect. 1587 * 1588 * @see #getSelectResults(QueryDataSet, int, int, boolean) 1589 * @param qds the QueryDataSet 1590 * @param singleRecord 1591 * @return a List of Record objects 1592 * @throws TorqueException Any exceptions caught during processing will be 1593 * rethrown wrapped into a TorqueException. 1594 */ 1595 public static List getSelectResults(QueryDataSet qds, boolean singleRecord) 1596 throws TorqueException 1597 { 1598 return getSelectResults(qds, 0, -1, singleRecord); 1599 } 1600 1601 /*** 1602 * Returns numberOfResults records in a QueryDataSet as a List 1603 * of Record objects. Starting at record 0. Used for 1604 * functionality like util.LargeSelect. 1605 * 1606 * @see #getSelectResults(QueryDataSet, int, int, boolean) 1607 * @param qds the QueryDataSet 1608 * @param numberOfResults 1609 * @param singleRecord 1610 * @return a List of Record objects 1611 * @throws TorqueException Any exceptions caught during processing will be 1612 * rethrown wrapped into a TorqueException. 1613 */ 1614 public static List getSelectResults( 1615 QueryDataSet qds, 1616 int numberOfResults, 1617 boolean singleRecord) 1618 throws TorqueException 1619 { 1620 List results = null; 1621 if (numberOfResults != 0) 1622 { 1623 results = getSelectResults(qds, 0, numberOfResults, singleRecord); 1624 } 1625 return results; 1626 } 1627 1628 /*** 1629 * Returns numberOfResults records in a QueryDataSet as a List 1630 * of Record objects. Starting at record start. Used for 1631 * functionality like util.LargeSelect. 1632 * 1633 * @param qds The <code>QueryDataSet</code> to extract results 1634 * from. 1635 * @param start The index from which to start retrieving 1636 * <code>Record</code> objects from the data set. 1637 * @param numberOfResults The number of results to return (or 1638 * <code> -1</code> for all results). 1639 * @param singleRecord Whether or not we want to select only a 1640 * single record. 1641 * @return A <code>List</code> of <code>Record</code> objects. 1642 * @exception TorqueException If any <code>Exception</code> occurs. 1643 */ 1644 public static List getSelectResults( 1645 QueryDataSet qds, 1646 int start, 1647 int numberOfResults, 1648 boolean singleRecord) 1649 throws TorqueException 1650 { 1651 List results = null; 1652 try 1653 { 1654 if (numberOfResults <= 0) 1655 { 1656 results = new ArrayList(); 1657 qds.fetchRecords(); 1658 } 1659 else 1660 { 1661 results = new ArrayList(numberOfResults); 1662 qds.fetchRecords(start, numberOfResults); 1663 } 1664 if (qds.size() > 1 && singleRecord) 1665 { 1666 handleMultipleRecords(qds); 1667 } 1668 1669 int startRecord = 0; 1670 1671 //Offset the correct number of people 1672 if (start > 0 && numberOfResults <= 0) 1673 { 1674 startRecord = start; 1675 } 1676 1677 // Return a List of Record objects. 1678 for (int i = startRecord; i < qds.size(); i++) 1679 { 1680 Record rec = qds.getRecord(i); 1681 results.add(rec); 1682 } 1683 } 1684 catch (Exception e) 1685 { 1686 throwTorqueException(e); 1687 } 1688 return results; 1689 } 1690 1691 /*** 1692 * Helper method which returns the primary key contained 1693 * in the given Criteria object. 1694 * 1695 * @param criteria A Criteria. 1696 * @return ColumnMap if the Criteria object contains a primary 1697 * key, or null if it doesn't. 1698 * @throws TorqueException Any exceptions caught during processing will be 1699 * rethrown wrapped into a TorqueException. 1700 */ 1701 private static ColumnMap getPrimaryKey(Criteria criteria) 1702 throws TorqueException 1703 { 1704 // Assume all the keys are for the same table. 1705 String key = (String) criteria.keys().nextElement(); 1706 1707 String table = criteria.getTableName(key); 1708 ColumnMap pk = null; 1709 1710 if (!table.equals("")) 1711 { 1712 DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); 1713 if (dbMap == null) 1714 { 1715 throw new TorqueException("dbMap is null"); 1716 } 1717 if (dbMap.getTable(table) == null) 1718 { 1719 throw new TorqueException("dbMap.getTable() is null"); 1720 } 1721 1722 ColumnMap[] columns = dbMap.getTable(table).getColumns(); 1723 1724 for (int i = 0; i < columns.length; i++) 1725 { 1726 if (columns[i].isPrimaryKey()) 1727 { 1728 pk = columns[i]; 1729 break; 1730 } 1731 } 1732 } 1733 return pk; 1734 } 1735 1736 /*** 1737 * Convenience method used to update rows in the DB. Checks if a 1738 * <i>single</i> int primary key is specified in the Criteria 1739 * object and uses it to perform the udpate. If no primary key is 1740 * specified an Exception will be thrown. 1741 * <p> 1742 * Use this method for performing an update of the kind: 1743 * <p> 1744 * "WHERE primary_key_id = an int" 1745 * <p> 1746 * To perform an update with non-primary key fields in the WHERE 1747 * clause use doUpdate(criteria, criteria). 1748 * 1749 * @param updateValues A Criteria object containing values used in 1750 * set clause. 1751 * @throws TorqueException Any exceptions caught during processing will be 1752 * rethrown wrapped into a TorqueException. 1753 */ 1754 public static void doUpdate(Criteria updateValues) throws TorqueException 1755 { 1756 Connection con = null; 1757 try 1758 { 1759 con = Transaction.beginOptional( 1760 updateValues.getDbName(), 1761 updateValues.isUseTransaction()); 1762 doUpdate(updateValues, con); 1763 Transaction.commit(con); 1764 } 1765 catch (TorqueException e) 1766 { 1767 Transaction.safeRollback(con); 1768 throw e; 1769 } 1770 } 1771 1772 /*** 1773 * Convenience method used to update rows in the DB. Checks if a 1774 * <i>single</i> int primary key is specified in the Criteria 1775 * object and uses it to perform the udpate. If no primary key is 1776 * specified an Exception will be thrown. 1777 * <p> 1778 * Use this method for performing an update of the kind: 1779 * <p> 1780 * "WHERE primary_key_id = an int" 1781 * <p> 1782 * To perform an update with non-primary key fields in the WHERE 1783 * clause use doUpdate(criteria, criteria). 1784 * 1785 * @param updateValues A Criteria object containing values used in 1786 * set clause. 1787 * @param con A Connection. 1788 * @throws TorqueException Any exceptions caught during processing will be 1789 * rethrown wrapped into a TorqueException. 1790 */ 1791 public static void doUpdate(Criteria updateValues, Connection con) 1792 throws TorqueException 1793 { 1794 ColumnMap pk = getPrimaryKey(updateValues); 1795 Criteria selectCriteria = null; 1796 1797 if (pk != null && updateValues.containsKey(pk.getFullyQualifiedName())) 1798 { 1799 selectCriteria = new Criteria(2); 1800 selectCriteria.put(pk.getFullyQualifiedName(), 1801 updateValues.remove(pk.getFullyQualifiedName())); 1802 } 1803 else 1804 { 1805 throw new TorqueException("No PK specified for database update"); 1806 } 1807 1808 doUpdate(selectCriteria, updateValues, con); 1809 } 1810 1811 /*** 1812 * Method used to update rows in the DB. Rows are selected based 1813 * on selectCriteria and updated using values in updateValues. 1814 * <p> 1815 * Use this method for performing an update of the kind: 1816 * <p> 1817 * WHERE some_column = some value AND could_have_another_column = 1818 * another value AND so on... 1819 * 1820 * @param selectCriteria A Criteria object containing values used in where 1821 * clause. 1822 * @param updateValues A Criteria object containing values used in set 1823 * clause. 1824 * @throws TorqueException Any exceptions caught during processing will be 1825 * rethrown wrapped into a TorqueException. 1826 */ 1827 public static void doUpdate(Criteria selectCriteria, Criteria updateValues) 1828 throws TorqueException 1829 { 1830 Connection con = null; 1831 try 1832 { 1833 con = Transaction.beginOptional(selectCriteria.getDbName(), 1834 updateValues.isUseTransaction()); 1835 doUpdate(selectCriteria, updateValues, con); 1836 Transaction.commit(con); 1837 } 1838 catch (TorqueException e) 1839 { 1840 Transaction.safeRollback(con); 1841 throw e; 1842 } 1843 } 1844 1845 /*** 1846 * Method used to update rows in the DB. Rows are selected based 1847 * on selectCriteria and updated using values in updateValues. 1848 * <p> 1849 * Use this method for performing an update of the kind: 1850 * <p> 1851 * WHERE some_column = some value AND could_have_another_column = 1852 * another value AND so on. 1853 * 1854 * @param selectCriteria A Criteria object containing values used in where 1855 * clause. 1856 * @param updateValues A Criteria object containing values used in set 1857 * clause. 1858 * @param con A Connection. 1859 * @throws TorqueException Any exceptions caught during processing will be 1860 * rethrown wrapped into a TorqueException. 1861 */ 1862 public static void doUpdate( 1863 Criteria selectCriteria, 1864 Criteria updateValues, 1865 Connection con) 1866 throws TorqueException 1867 { 1868 DB db = Torque.getDB(selectCriteria.getDbName()); 1869 DatabaseMap dbMap = Torque.getDatabaseMap(selectCriteria.getDbName()); 1870 1871 // Set up a list of required tables. StringStack.add() 1872 // only adds element if it is unique. 1873 HashSet tables = new HashSet(); 1874 Iterator it = selectCriteria.keySet().iterator(); 1875 while (it.hasNext()) 1876 { 1877 tables.add(selectCriteria.getTableName((String) it.next())); 1878 } 1879 1880 Iterator tabIt = tables.iterator(); 1881 while (tabIt.hasNext()) 1882 { 1883 String tab = (String) tabIt.next(); 1884 KeyDef kd = new KeyDef(); 1885 HashSet whereClause = new HashSet(); 1886 DatabaseMap tempDbMap = dbMap; 1887 1888 ColumnMap[] columnMaps = tempDbMap.getTable(tab).getColumns(); 1889 for (int j = 0; j < columnMaps.length; j++) 1890 { 1891 ColumnMap colMap = columnMaps[j]; 1892 if (colMap.isPrimaryKey()) 1893 { 1894 kd.addAttrib(colMap.getColumnName()); 1895 } 1896 String key = new StringBuffer(colMap.getTableName()) 1897 .append('.') 1898 .append(colMap.getColumnName()) 1899 .toString(); 1900 if (selectCriteria.containsKey(key)) 1901 { 1902 if (selectCriteria 1903 .getComparison(key) 1904 .equals(Criteria.CUSTOM)) 1905 { 1906 whereClause.add(selectCriteria.getString(key)); 1907 } 1908 else 1909 { 1910 whereClause.add( 1911 SqlExpression.build( 1912 colMap.getColumnName(), 1913 selectCriteria.getValue(key), 1914 selectCriteria.getComparison(key), 1915 selectCriteria.isIgnoreCase(), 1916 db)); 1917 } 1918 } 1919 } 1920 TableDataSet tds = null; 1921 try 1922 { 1923 // Get affected records. 1924 tds = new TableDataSet(con, tab, kd); 1925 String sqlSnippet = StringUtils.join(whereClause.iterator(), " AND "); 1926 if (log.isDebugEnabled()) 1927 { 1928 log.debug("BasePeer.doUpdate: whereClause=" + sqlSnippet); 1929 } 1930 tds.where(sqlSnippet); 1931 tds.fetchRecords(); 1932 1933 if (tds.size() > 1 && selectCriteria.isSingleRecord()) 1934 { 1935 handleMultipleRecords(tds); 1936 } 1937 for (int j = 0; j < tds.size(); j++) 1938 { 1939 Record rec = tds.getRecord(j); 1940 BasePeer.insertOrUpdateRecord(rec, tab, updateValues); 1941 } 1942 } 1943 catch (Exception e) 1944 { 1945 throwTorqueException(e); 1946 } 1947 finally 1948 { 1949 if (tds != null) 1950 { 1951 try 1952 { 1953 tds.close(); 1954 } 1955 catch (Exception e) 1956 { 1957 throwTorqueException(e); 1958 } 1959 } 1960 } 1961 } 1962 } 1963 1964 /*** 1965 * Utility method which executes a given sql statement. This 1966 * method should be used for update, insert, and delete 1967 * statements. Use executeQuery() for selects. 1968 * 1969 * @param stmt A String with the sql statement to execute. 1970 * @return The number of rows affected. 1971 * @throws TorqueException Any exceptions caught during processing will be 1972 * rethrown wrapped into a TorqueException. 1973 */ 1974 public static int executeStatement(String stmt) throws TorqueException 1975 { 1976 return executeStatement(stmt, Torque.getDefaultDB()); 1977 } 1978 1979 /*** 1980 * Utility method which executes a given sql statement. This 1981 * method should be used for update, insert, and delete 1982 * statements. Use executeQuery() for selects. 1983 * 1984 * @param stmt A String with the sql statement to execute. 1985 * @param dbName Name of database to connect to. 1986 * @return The number of rows affected. 1987 * @throws TorqueException Any exceptions caught during processing will be 1988 * rethrown wrapped into a TorqueException. 1989 */ 1990 public static int executeStatement(String stmt, String dbName) 1991 throws TorqueException 1992 { 1993 Connection db = null; 1994 int rowCount = -1; 1995 try 1996 { 1997 db = Torque.getConnection(dbName); 1998 rowCount = executeStatement(stmt, db); 1999 } 2000 finally 2001 { 2002 Torque.closeConnection(db); 2003 } 2004 return rowCount; 2005 } 2006 2007 /*** 2008 * Utility method which executes a given sql statement. This 2009 * method should be used for update, insert, and delete 2010 * statements. Use executeQuery() for selects. 2011 * 2012 * @param stmt A String with the sql statement to execute. 2013 * @param con A Connection. 2014 * @return The number of rows affected. 2015 * @throws TorqueException Any exceptions caught during processing will be 2016 * rethrown wrapped into a TorqueException. 2017 */ 2018 public static int executeStatement(String stmt, Connection con) 2019 throws TorqueException 2020 { 2021 int rowCount = -1; 2022 Statement statement = null; 2023 try 2024 { 2025 statement = con.createStatement(); 2026 rowCount = statement.executeUpdate(stmt); 2027 } 2028 catch (SQLException e) 2029 { 2030 throw new TorqueException(e); 2031 } 2032 finally 2033 { 2034 if (statement != null) 2035 { 2036 try 2037 { 2038 statement.close(); 2039 } 2040 catch (SQLException e) 2041 { 2042 throw new TorqueException(e); 2043 } 2044 } 2045 } 2046 return rowCount; 2047 } 2048 2049 /*** 2050 * If the user specified that (s)he only wants to retrieve a 2051 * single record and multiple records are retrieved, this method 2052 * is called to handle the situation. The default behavior is to 2053 * throw an exception, but subclasses can override this method as 2054 * needed. 2055 * 2056 * @param ds The DataSet which contains multiple records. 2057 * @exception TorqueException Couldn't handle multiple records. 2058 */ 2059 protected static void handleMultipleRecords(DataSet ds) 2060 throws TorqueException 2061 { 2062 throw new TorqueException("Criteria expected single Record and " 2063 + "Multiple Records were selected"); 2064 } 2065 2066 /*** 2067 * This method returns the MapBuilder specified in the 2068 * configuration file. By default, this is 2069 * org.apache.torque.util.db.map.TurbineMapBuilder. 2070 * FIXME! With the decoupled Torque there seem to be no 2071 * default map builder anymore. 2072 * 2073 * @return A MapBuilder. 2074 * @throws TorqueException Any exceptions caught during processing will be 2075 * rethrown wrapped into a TorqueException. 2076 * @deprecated you have to specify the name of the map builder! 2077 */ 2078 public static MapBuilder getMapBuilder() throws TorqueException 2079 { 2080 return getMapBuilder(DEFAULT_MAP_BUILDER.trim()); 2081 } 2082 2083 /*** 2084 * This method returns the MapBuilder specified in the name 2085 * parameter. You should pass in the full path to the class, ie: 2086 * org.apache.torque.util.db.map.TurbineMapBuilder. The 2087 * MapBuilder instances are cached in this class for speed. 2088 * 2089 * @param name name of the MapBuilder 2090 * @return A MapBuilder, or null (and logs the error) if the 2091 * MapBuilder was not found. 2092 */ 2093 public static MapBuilder getMapBuilder(String name) 2094 { 2095 try 2096 { 2097 MapBuilder mb = (MapBuilder) mapBuilders.get(name); 2098 // Use the 'double-check pattern' for syncing 2099 // caching of the MapBuilder. 2100 if (mb == null) 2101 { 2102 synchronized (mapBuilders) 2103 { 2104 mb = (MapBuilder) mapBuilders.get(name); 2105 if (mb == null) 2106 { 2107 mb = (MapBuilder) Class.forName(name).newInstance(); 2108 // Cache the MapBuilder before it is built. 2109 mapBuilders.put(name, mb); 2110 } 2111 } 2112 } 2113 2114 // Build the MapBuilder in its own synchronized block to 2115 // avoid locking up the whole Hashtable while doing so. 2116 // Note that *all* threads need to do a sync check on isBuilt() 2117 // to avoid grabing an uninitialized MapBuilder. This, however, 2118 // is a relatively fast operation. 2119 synchronized (mb) 2120 { 2121 if (!mb.isBuilt()) 2122 { 2123 try 2124 { 2125 mb.doBuild(); 2126 } 2127 catch (Exception e) 2128 { 2129 // need to think about whether we'd want to remove 2130 // the MapBuilder from the cache if it can't be 2131 // built correctly...? pgo 2132 throw e; 2133 } 2134 } 2135 } 2136 return mb; 2137 } 2138 catch (Exception e) 2139 { 2140 // Have to catch possible exceptions because method is 2141 // used in initialization of Peers. Log the exception and 2142 // return null. 2143 String message = 2144 "BasePeer.MapBuilder failed trying to instantiate: " + name; 2145 log.error(message, e); 2146 } 2147 return null; 2148 } 2149 2150 /*** 2151 * Performs a SQL <code>select</code> using a PreparedStatement. 2152 * Note: this method does not handle null criteria values. 2153 * 2154 * @param criteria 2155 * @param con 2156 * @return 2157 * @throws TorqueException Error performing database query. 2158 */ 2159 public static List doPSSelect(Criteria criteria, Connection con) 2160 throws TorqueException 2161 { 2162 List v = null; 2163 2164 StringBuffer qry = new StringBuffer(); 2165 List params = new ArrayList(criteria.size()); 2166 2167 createPreparedStatement(criteria, qry, params); 2168 2169 PreparedStatement stmt = null; 2170 try 2171 { 2172 stmt = con.prepareStatement(qry.toString()); 2173 2174 for (int i = 0; i < params.size(); i++) 2175 { 2176 Object param = params.get(i); 2177 if (param instanceof java.sql.Date) 2178 { 2179 stmt.setDate(i + 1, (java.sql.Date) param); 2180 } 2181 else if (param instanceof NumberKey) 2182 { 2183 stmt.setBigDecimal(i + 1, 2184 ((NumberKey) param).getBigDecimal()); 2185 } 2186 else 2187 { 2188 stmt.setString(i + 1, param.toString()); 2189 } 2190 } 2191 2192 QueryDataSet qds = null; 2193 try 2194 { 2195 qds = new QueryDataSet(stmt.executeQuery()); 2196 v = getSelectResults(qds); 2197 } 2198 finally 2199 { 2200 if (qds != null) 2201 { 2202 qds.close(); 2203 } 2204 } 2205 } 2206 catch (Exception e) 2207 { 2208 throwTorqueException(e); 2209 } 2210 finally 2211 { 2212 if (stmt != null) 2213 { 2214 try 2215 { 2216 stmt.close(); 2217 } 2218 catch (SQLException e) 2219 { 2220 throw new TorqueException(e); 2221 } 2222 } 2223 } 2224 return v; 2225 } 2226 2227 /*** 2228 * Do a Prepared Statement select according to the given criteria 2229 * 2230 * @param criteria 2231 * @return 2232 * @throws TorqueException Any exceptions caught during processing will be 2233 * rethrown wrapped into a TorqueException. 2234 */ 2235 public static List doPSSelect(Criteria criteria) throws TorqueException 2236 { 2237 Connection con = Torque.getConnection(criteria.getDbName()); 2238 List v = null; 2239 2240 try 2241 { 2242 v = doPSSelect(criteria, con); 2243 } 2244 finally 2245 { 2246 Torque.closeConnection(con); 2247 } 2248 return v; 2249 } 2250 2251 /*** 2252 * Create a new PreparedStatement. It builds a string representation 2253 * of a query and a list of PreparedStatement parameters. 2254 * 2255 * @param criteria 2256 * @param queryString 2257 * @param params 2258 * @throws TorqueException Any exceptions caught during processing will be 2259 * rethrown wrapped into a TorqueException. 2260 */ 2261 public static void createPreparedStatement( 2262 Criteria criteria, 2263 StringBuffer queryString, 2264 List params) 2265 throws TorqueException 2266 { 2267 DB db = Torque.getDB(criteria.getDbName()); 2268 DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName()); 2269 2270 Query query = new Query(); 2271 2272 UniqueList selectModifiers = query.getSelectModifiers(); 2273 UniqueList selectClause = query.getSelectClause(); 2274 UniqueList fromClause = query.getFromClause(); 2275 UniqueList whereClause = query.getWhereClause(); 2276 UniqueList orderByClause = query.getOrderByClause(); 2277 2278 UniqueList orderBy = criteria.getOrderByColumns(); 2279 UniqueList select = criteria.getSelectColumns(); 2280 Hashtable aliases = criteria.getAsColumns(); 2281 UniqueList modifiers = criteria.getSelectModifiers(); 2282 2283 for (int i = 0; i < modifiers.size(); i++) 2284 { 2285 selectModifiers.add(modifiers.get(i)); 2286 } 2287 2288 for (int i = 0; i < select.size(); i++) 2289 { 2290 String columnName = (String) select.get(i); 2291 if (columnName.indexOf('.') == -1) 2292 { 2293 throwMalformedColumnNameException("select", columnName); 2294 } 2295 String tableName = null; 2296 selectClause.add(columnName); 2297 int parenPos = columnName.indexOf('('); 2298 if (parenPos == -1) 2299 { 2300 tableName = columnName.substring(0, columnName.indexOf('.')); 2301 } 2302 else 2303 { 2304 tableName = 2305 columnName.substring(parenPos + 1, columnName.indexOf('.')); 2306 // functions may contain qualifiers so only take the last 2307 // word as the table name. 2308 int lastSpace = tableName.lastIndexOf(' '); 2309 if (lastSpace != -1) 2310 { 2311 tableName = tableName.substring(lastSpace + 1); 2312 } 2313 } 2314 String tableName2 = criteria.getTableForAlias(tableName); 2315 if (tableName2 != null) 2316 { 2317 fromClause.add(new StringBuffer(tableName.length() 2318 + tableName2.length() + 1) 2319 .append(tableName2) 2320 .append(' ') 2321 .append(tableName) 2322 .toString()); 2323 } 2324 else 2325 { 2326 fromClause.add(tableName); 2327 } 2328 } 2329 2330 Iterator it = aliases.keySet().iterator(); 2331 while (it.hasNext()) 2332 { 2333 String key = (String) it.next(); 2334 selectClause.add((String) aliases.get(key) + " AS " + key); 2335 } 2336 2337 Iterator critKeys = criteria.keySet().iterator(); 2338 while (critKeys.hasNext()) 2339 { 2340 String key = (String) critKeys.next(); 2341 Criteria.Criterion criterion = criteria.getCriterion(key); 2342 Criteria.Criterion[] someCriteria = 2343 criterion.getAttachedCriterion(); 2344 2345 String table = null; 2346 for (int i = 0; i < someCriteria.length; i++) 2347 { 2348 String tableName = someCriteria[i].getTable(); 2349 table = criteria.getTableForAlias(tableName); 2350 if (table != null) 2351 { 2352 fromClause.add(new StringBuffer(tableName.length() 2353 + table.length() + 1) 2354 .append(table) 2355 .append(' ') 2356 .append(tableName) 2357 .toString()); 2358 } 2359 else 2360 { 2361 fromClause.add(tableName); 2362 table = tableName; 2363 } 2364 2365 boolean ignorCase = ((criteria.isIgnoreCase() 2366 || someCriteria[i].isIgnoreCase()) 2367 && (dbMap 2368 .getTable(table) 2369 .getColumn(someCriteria[i].getColumn()) 2370 .getType() 2371 instanceof String)); 2372 2373 someCriteria[i].setIgnoreCase(ignorCase); 2374 } 2375 2376 criterion.setDB(db); 2377 StringBuffer sb = new StringBuffer(); 2378 criterion.appendPsTo(sb, params); 2379 whereClause.add(sb.toString()); 2380 } 2381 2382 List join = criteria.getJoinL(); 2383 if (join != null) 2384 { 2385 for (int i = 0; i < join.size(); i++) 2386 { 2387 String join1 = (String) join.get(i); 2388 String join2 = (String) criteria.getJoinR().get(i); 2389 if (join1.indexOf('.') == -1) 2390 { 2391 throwMalformedColumnNameException("join", join1); 2392 } 2393 if (join2.indexOf('.') == -1) 2394 { 2395 throwMalformedColumnNameException("join", join2); 2396 } 2397 2398 String tableName = join1.substring(0, join1.indexOf('.')); 2399 String table = criteria.getTableForAlias(tableName); 2400 if (table != null) 2401 { 2402 fromClause.add(new StringBuffer(tableName.length() 2403 + table.length() + 1) 2404 .append(table) 2405 .append(' ') 2406 .append(tableName) 2407 .toString()); 2408 } 2409 else 2410 { 2411 fromClause.add(tableName); 2412 } 2413 2414 int dot = join2.indexOf('.'); 2415 tableName = join2.substring(0, dot); 2416 table = criteria.getTableForAlias(tableName); 2417 if (table != null) 2418 { 2419 fromClause.add(new StringBuffer(tableName.length() 2420 + table.length() + 1) 2421 .append(table) 2422 .append(' ') 2423 .append(tableName) 2424 .toString()); 2425 } 2426 else 2427 { 2428 fromClause.add(tableName); 2429 table = tableName; 2430 } 2431 2432 boolean ignorCase = (criteria.isIgnoreCase() 2433 && (dbMap 2434 .getTable(table) 2435 .getColumn(join2.substring(dot + 1, join2.length())) 2436 .getType() 2437 instanceof String)); 2438 2439 whereClause.add( 2440 SqlExpression.buildInnerJoin(join1, join2, ignorCase, db)); 2441 } 2442 } 2443 2444 if (orderBy != null && orderBy.size() > 0) 2445 { 2446 // Check for each String/Character column and apply 2447 // toUpperCase(). 2448 for (int i = 0; i < orderBy.size(); i++) 2449 { 2450 String orderByColumn = (String) orderBy.get(i); 2451 if (orderByColumn.indexOf('.') == -1) 2452 { 2453 throwMalformedColumnNameException("order by", 2454 orderByColumn); 2455 } 2456 String table = 2457 orderByColumn.substring(0, orderByColumn.indexOf('.')); 2458 // See if there's a space (between the column list and sort 2459 // order in ORDER BY table.column DESC). 2460 int spacePos = orderByColumn.indexOf(' '); 2461 String columnName; 2462 if (spacePos == -1) 2463 { 2464 columnName = 2465 orderByColumn.substring(orderByColumn.indexOf('.') + 1); 2466 } 2467 else 2468 { 2469 columnName = orderByColumn.substring( 2470 orderByColumn.indexOf('.') + 1, 2471 spacePos); 2472 } 2473 ColumnMap column = dbMap.getTable(table).getColumn(columnName); 2474 if (column.getType() instanceof String) 2475 { 2476 if (spacePos == -1) 2477 { 2478 orderByClause.add( 2479 db.ignoreCaseInOrderBy(orderByColumn)); 2480 } 2481 else 2482 { 2483 orderByClause.add(db.ignoreCaseInOrderBy( 2484 orderByColumn.substring(0, spacePos)) 2485 + orderByColumn.substring(spacePos)); 2486 } 2487 selectClause.add( 2488 db.ignoreCaseInOrderBy(table + '.' + columnName)); 2489 } 2490 else 2491 { 2492 orderByClause.add(orderByColumn); 2493 } 2494 } 2495 } 2496 2497 // Limit the number of rows returned. 2498 int limit = criteria.getLimit(); 2499 int offset = criteria.getOffset(); 2500 String limitString = null; 2501 if (offset > 0 && db.supportsNativeOffset() 2502 && db.getLimitStyle() != DB.LIMIT_STYLE_ORACLE) 2503 { 2504 switch (db.getLimitStyle()) 2505 { 2506 case DB.LIMIT_STYLE_MYSQL : 2507 limitString = new StringBuffer() 2508 .append(offset) 2509 .append(", ") 2510 .append(limit) 2511 .toString(); 2512 break; 2513 case DB.LIMIT_STYLE_POSTGRES : 2514 limitString = new StringBuffer() 2515 .append(limit) 2516 .append(" offset ") 2517 .append(offset) 2518 .toString(); 2519 break; 2520 } 2521 2522 // Now set the criteria's limit and offset to return the 2523 // full resultset since the results are limited on the 2524 // server. 2525 criteria.setLimit(-1); 2526 criteria.setOffset(0); 2527 } 2528 else if (limit > 0 && db.supportsNativeLimit() 2529 && db.getLimitStyle() != DB.LIMIT_STYLE_ORACLE) 2530 { 2531 limitString = String.valueOf(limit); 2532 2533 // Now set the criteria's limit to return the full 2534 // resultset since the results are limited on the server. 2535 criteria.setLimit(-1); 2536 } 2537 2538 if (limitString != null) 2539 { 2540 switch (db.getLimitStyle()) 2541 { 2542 /* Don't have a Sybase install to validate this against(dlr) 2543 case DB.LIMIT_STYLE_SYBASE: 2544 query.setRowcount(limitString); 2545 break; 2546 */ 2547 default : 2548 query.setLimit(limitString); 2549 } 2550 } 2551 2552 String sql; 2553 if ((limit > 0 || offset > 0) 2554 && db.getLimitStyle() == DB.LIMIT_STYLE_ORACLE) 2555 { 2556 sql = createOracleLimitOffsetQuery(query, limit, offset); 2557 criteria.setLimit(-1); 2558 criteria.setOffset(0); 2559 } 2560 else 2561 { 2562 sql = query.toString(); 2563 } 2564 2565 if (log.isDebugEnabled()) 2566 { 2567 log.debug(sql); 2568 } 2569 queryString.append(sql); 2570 } 2571 2572 /*** 2573 * Throws a TorqueException with the malformed column name error 2574 * message. The error message looks like this:<p> 2575 * 2576 * <code> 2577 * Malformed column name in Criteria [criteriaPhrase]: 2578 * '[columnName]' is not of the form 'table.column' 2579 * </code> 2580 * 2581 * @param criteriaPhrase a String, one of "select", "join", or "order by" 2582 * @param columnName a String containing the offending column name 2583 * @throws TorqueException Any exceptions caught during processing will be 2584 * rethrown wrapped into a TorqueException. 2585 */ 2586 private static void throwMalformedColumnNameException( 2587 String criteriaPhrase, 2588 String columnName) 2589 throws TorqueException 2590 { 2591 throw new TorqueException("Malformed column name in Criteria " 2592 + criteriaPhrase 2593 + ": '" 2594 + columnName 2595 + "' is not of the form 'table.column'"); 2596 } 2597 }

This page was automatically generated by Maven