View Javadoc
1 package org.apache.torque.oid; 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.math.BigDecimal; 58 import java.sql.Connection; 59 import java.sql.ResultSet; 60 import java.sql.Statement; 61 import java.util.ArrayList; 62 import java.util.Hashtable; 63 import java.util.Iterator; 64 import java.util.List; 65 66 import org.apache.commons.configuration.Configuration; 67 68 import org.apache.commons.logging.Log; 69 import org.apache.commons.logging.LogFactory; 70 71 import org.apache.torque.Torque; 72 import org.apache.torque.TorqueException; 73 import org.apache.torque.map.DatabaseMap; 74 import org.apache.torque.map.TableMap; 75 import org.apache.torque.util.Transaction; 76 77 //!! 78 // NOTE: 79 // It would be nice to decouple this from 80 // Torque. This is a great stand-alone utility. 81 82 /*** 83 * This method of ID generation is used to ensure that code is 84 * more database independent. For example, MySQL has an auto-increment 85 * feature while Oracle uses sequences. It caches several ids to 86 * avoid needing a Connection for every request. 87 * 88 * This class uses the table ID_TABLE defined in 89 * conf/master/id-table-schema.xml. The columns in ID_TABLE are used as 90 * follows:<br> 91 * 92 * ID_TABLE_ID - The PK for this row (any unique int).<br> 93 * TABLE_NAME - The name of the table you want ids for.<br> 94 * NEXT_ID - The next id returned by IDBroker when it queries the 95 * database (not when it returns an id from memory).<br> 96 * QUANTITY - The number of ids that IDBroker will cache in memory.<br> 97 * <p> 98 * Use this class like this: 99 * <pre> 100 * int id = dbMap.getIDBroker().getNextIdAsInt(null, "TABLE_NAME"); 101 * - or - 102 * BigDecimal[] ids = ((IDBroker)dbMap.getIDBroker()) 103 * .getNextIds("TABLE_NAME", numOfIdsToReturn); 104 * </pre> 105 * 106 * NOTE: When the ID_TABLE must be updated we must ensure that 107 * IDBroker objects running in different JVMs do not overwrite each 108 * other. This is accomplished using using the transactional support 109 * occuring in some databases. Using this class with a database that 110 * does not support transactions should be limited to a single JVM. 111 * 112 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a> 113 * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a> 114 * @version $Id: IDBroker.java,v 1.27 2003/08/25 16:33:22 henning Exp $ 115 */ 116 public class IDBroker implements Runnable, IdGenerator 117 { 118 /*** 119 * Name of the ID_TABLE = ID_TABLE 120 */ 121 public static final String ID_TABLE = "ID_TABLE"; 122 123 /*** 124 * Fully qualified Table_Name column name 125 */ 126 public static final String TABLE_NAME = ID_TABLE + ".TABLE_NAME"; 127 128 /*** 129 * Fully qualified Next_ID column name 130 */ 131 public static final String NEXT_ID = ID_TABLE + ".NEXT_ID"; 132 133 /*** 134 * Fully qualified Quantity column name 135 */ 136 public static final String QUANTITY = ID_TABLE + ".QUANTITY"; 137 138 /*** The TableMap referencing the ID_TABLE for this IDBroker. */ 139 private TableMap tableMap; 140 141 /*** 142 * The default size of the per-table meta data <code>Hashtable</code> 143 * objects. 144 */ 145 private static final int DEFAULT_SIZE = 40; 146 147 /*** 148 * The cached IDs for each table. 149 * 150 * Key: String table name. 151 * Value: List of Integer IDs. 152 */ 153 private Hashtable ids = new Hashtable(DEFAULT_SIZE); 154 155 /*** 156 * The quantity of ids to grab for each table. 157 * 158 * Key: String table name. 159 * Value: Integer quantity. 160 */ 161 private Hashtable quantityStore = new Hashtable(DEFAULT_SIZE); 162 163 /*** 164 * The last time this IDBroker queried the database for ids. 165 * 166 * Key: String table name. 167 * Value: Date of last id request. 168 */ 169 private Hashtable lastQueryTime = new Hashtable(DEFAULT_SIZE); 170 171 /*** 172 * Amount of time for the thread to sleep 173 */ 174 private static final int SLEEP_PERIOD = 1 * 60000; 175 176 /*** 177 * The safety Margin 178 */ 179 private static final float SAFETY_MARGIN = 1.2f; 180 181 /*** 182 * The houseKeeperThread thread 183 */ 184 private Thread houseKeeperThread = null; 185 186 /*** 187 * Are transactions supported? 188 */ 189 private boolean transactionsSupported = false; 190 191 /*** 192 * The value of ONE! 193 */ 194 private static final BigDecimal ONE = new BigDecimal("1"); 195 196 /*** the configuration */ 197 private Configuration configuration; 198 199 /*** property name */ 200 private static final String DB_IDBROKER_CLEVERQUANTITY = 201 "idbroker.clever.quantity"; 202 203 /*** property name */ 204 private static final String DB_IDBROKER_PREFETCH = 205 "idbroker.prefetch"; 206 207 /*** property name */ 208 private static final String DB_IDBROKER_USENEWCONNECTION = 209 "idbroker.usenewconnection"; 210 211 /*** the log */ 212 private Log log = LogFactory.getLog(IDBroker.class); 213 214 /*** 215 * Creates an IDBroker for the ID table. 216 * 217 * @param tMap A TableMap. 218 */ 219 public IDBroker(TableMap tMap) 220 { 221 this.tableMap = tMap; 222 configuration = Torque.getConfiguration(); 223 224 // Start the housekeeper thread only if prefetch has not been disabled 225 if (configuration.getBoolean(DB_IDBROKER_PREFETCH, true)) 226 { 227 houseKeeperThread = new Thread(this); 228 // Indicate that this is a system thread. JVM will quit only when 229 // there are no more active user threads. Settings threads spawned 230 // internally by Torque as daemons allows commandline applications 231 // using Torque terminate in an orderly manner. 232 houseKeeperThread.setDaemon(true); 233 houseKeeperThread.start(); 234 } 235 236 // Check for Transaction support. Give warning message if 237 // IDBroker is being used with a database that does not 238 // support transactions. 239 String dbName = tMap.getDatabaseMap().getName(); 240 Connection dbCon = null; 241 try 242 { 243 dbCon = Torque.getConnection(dbName); 244 transactionsSupported = dbCon.getMetaData().supportsTransactions(); 245 } 246 catch (Exception e) 247 { 248 transactionsSupported = false; 249 } 250 finally 251 { 252 try 253 { 254 // Return the connection to the pool. 255 dbCon.close(); 256 } 257 catch (Exception e) 258 { 259 } 260 } 261 if (!transactionsSupported) 262 { 263 log.warn("IDBroker is being used with db '" + dbName 264 + "', which does not support transactions. IDBroker " 265 + "attempts to use transactions to limit the possibility " 266 + "of duplicate key generation. Without transactions, " 267 + "duplicate key generation is possible if multiple JVMs " 268 + "are used or other means are used to write to the " 269 + "database."); 270 } 271 } 272 273 /*** 274 * Set the configuration 275 * 276 * @param configuration the configuration 277 */ 278 public void setConfiguration(Configuration configuration) 279 { 280 this.configuration = configuration; 281 } 282 283 /*** 284 * Returns an id as a primitive int. Note this method does not 285 * require a Connection, it just implements the KeyGenerator 286 * interface. if a Connection is needed one will be requested. 287 * To force the use of the passed in connection set the configuration 288 * property torque.idbroker.usenewconnection = false 289 * 290 * @param connection A Connection. 291 * @param tableName an Object that contains additional info. 292 * @return An int with the value for the id. 293 * @exception Exception Database error. 294 */ 295 public int getIdAsInt(Connection connection, Object tableName) 296 throws Exception 297 { 298 return getIdAsBigDecimal(connection, tableName).intValue(); 299 } 300 301 302 /*** 303 * Returns an id as a primitive long. Note this method does not 304 * require a Connection, it just implements the KeyGenerator 305 * interface. if a Connection is needed one will be requested. 306 * To force the use of the passed in connection set the configuration 307 * property torque.idbroker.usenewconnection = false 308 * 309 * @param connection A Connection. 310 * @param tableName a String that identifies a table. 311 * @return A long with the value for the id. 312 * @exception Exception Database error. 313 */ 314 public long getIdAsLong(Connection connection, Object tableName) 315 throws Exception 316 { 317 return getIdAsBigDecimal(connection, tableName).longValue(); 318 } 319 320 /*** 321 * Returns an id as a BigDecimal. Note this method does not 322 * require a Connection, it just implements the KeyGenerator 323 * interface. if a Connection is needed one will be requested. 324 * To force the use of the passed in connection set the configuration 325 * property torque.idbroker.usenewconnection = false 326 * 327 * @param connection A Connection. 328 * @param tableName a String that identifies a table.. 329 * @return A BigDecimal id. 330 * @exception Exception Database error. 331 */ 332 public BigDecimal getIdAsBigDecimal(Connection connection, 333 Object tableName) 334 throws Exception 335 { 336 BigDecimal[] id = getNextIds((String) tableName, 1, connection); 337 return id[0]; 338 } 339 340 /*** 341 * Returns an id as a String. Note this method does not 342 * require a Connection, it just implements the KeyGenerator 343 * interface. if a Connection is needed one will be requested. 344 * To force the use of the passed in connection set the configuration 345 * property torque.idbroker.usenewconnection = false 346 * 347 * @param connection A Connection should be null. 348 * @param tableName a String that identifies a table. 349 * @return A String id 350 * @exception Exception Database error. 351 */ 352 public String getIdAsString(Connection connection, Object tableName) 353 throws Exception 354 { 355 return getIdAsBigDecimal(connection, tableName).toString(); 356 } 357 358 359 /*** 360 * A flag to determine the timing of the id generation * 361 * @return a <code>boolean</code> value 362 */ 363 public boolean isPriorToInsert() 364 { 365 return true; 366 } 367 368 /*** 369 * A flag to determine the timing of the id generation 370 * 371 * @return a <code>boolean</code> value 372 */ 373 public boolean isPostInsert() 374 { 375 return false; 376 } 377 378 /*** 379 * A flag to determine whether a Connection is required to 380 * generate an id. 381 * 382 * @return a <code>boolean</code> value 383 */ 384 public boolean isConnectionRequired() 385 { 386 return false; 387 } 388 389 /*** 390 * This method returns x number of ids for the given table. 391 * 392 * @param tableName The name of the table for which we want an id. 393 * @param numOfIdsToReturn The desired number of ids. 394 * @return A BigDecimal. 395 * @exception Exception Database error. 396 */ 397 public synchronized BigDecimal[] getNextIds(String tableName, 398 int numOfIdsToReturn) 399 throws Exception 400 { 401 return getNextIds(tableName, numOfIdsToReturn, null); 402 } 403 404 /*** 405 * This method returns x number of ids for the given table. 406 * Note this method does not require a Connection. 407 * If a Connection is needed one will be requested. 408 * To force the use of the passed in connection set the configuration 409 * property torque.idbroker.usenewconnection = false 410 * 411 * @param tableName The name of the table for which we want an id. 412 * @param numOfIdsToReturn The desired number of ids. 413 * @param connection A Connection. 414 * @return A BigDecimal. 415 * @exception Exception Database error. 416 */ 417 public synchronized BigDecimal[] getNextIds(String tableName, 418 int numOfIdsToReturn, 419 Connection connection) 420 throws Exception 421 { 422 if (tableName == null) 423 { 424 throw new Exception("getNextIds(): tableName == null"); 425 } 426 427 // A note about the synchronization: I (jmcnally) looked at 428 // the synchronized blocks to avoid thread issues that were 429 // being used in this and the storeId method. I do not think 430 // they were being effective, so I synchronized the method. 431 // I have left the blocks that did exist commented in the code 432 // to make it easier for others to take a look, because it 433 // would be preferrable to avoid the synchronization on the 434 // method 435 436 List availableIds = (List) ids.get(tableName); 437 438 if (availableIds == null || availableIds.size() < numOfIdsToReturn) 439 { 440 if (availableIds == null) 441 { 442 log.debug("Forced id retrieval - no available list"); 443 } 444 else 445 { 446 log.debug("Forced id retrieval - " + availableIds.size()); 447 } 448 storeIDs(tableName, true, connection); 449 availableIds = (List) ids.get(tableName); 450 } 451 452 int size = availableIds.size() < numOfIdsToReturn 453 ? availableIds.size() : numOfIdsToReturn; 454 455 BigDecimal[] results = new BigDecimal[size]; 456 457 // We assume that availableIds will always come from the ids 458 // Hashtable and would therefore always be the same object for 459 // a specific table. 460 // synchronized (availableIds) 461 // { 462 for (int i = size - 1; i >= 0; i--) 463 { 464 results[i] = (BigDecimal) availableIds.get(i); 465 availableIds.remove(i); 466 } 467 // } 468 469 return results; 470 } 471 472 /*** 473 * Describe <code>exists</code> method here. 474 * 475 * @param tableName a <code>String</code> value that is used to identify 476 * the row 477 * @return a <code>boolean</code> value 478 * @exception TorqueException if an error occurs 479 * @exception Exception a generic exception. 480 */ 481 public boolean exists(String tableName) 482 throws TorqueException, Exception 483 { 484 String query = new StringBuffer(100) 485 .append("select ") 486 .append(TABLE_NAME) 487 .append(" where ") 488 .append(TABLE_NAME).append("='").append(tableName).append('\'') 489 .toString(); 490 491 boolean exists = false; 492 Connection dbCon = null; 493 try 494 { 495 String databaseName = tableMap.getDatabaseMap().getName(); 496 497 dbCon = Torque.getConnection(databaseName); 498 Statement statement = dbCon.createStatement(); 499 ResultSet rs = statement.executeQuery(query); 500 exists = rs.next(); 501 statement.close(); 502 } 503 finally 504 { 505 // Return the connection to the pool. 506 try 507 { 508 dbCon.close(); 509 } 510 catch (Exception e) 511 { 512 log.error("Release of connection failed.", e); 513 } 514 } 515 return exists; 516 } 517 518 /*** 519 * A background thread that tries to ensure that when someone asks 520 * for ids, that there are already some loaded and that the 521 * database is not accessed. 522 */ 523 public void run() 524 { 525 log.debug("IDBroker thread was started."); 526 527 Thread thisThread = Thread.currentThread(); 528 while (houseKeeperThread == thisThread) 529 { 530 try 531 { 532 houseKeeperThread.sleep(SLEEP_PERIOD); 533 } 534 catch (InterruptedException exc) 535 { 536 // ignored 537 } 538 539 // logger.info("IDBroker thread checking for more keys."); 540 Iterator it = ids.keySet().iterator(); 541 while (it.hasNext()) 542 { 543 String tableName = (String) it.next(); 544 if (log.isDebugEnabled()) 545 { 546 log.debug("IDBroker thread checking for more keys " 547 + "on table: " + tableName); 548 } 549 List availableIds = (List) ids.get(tableName); 550 int quantity = getQuantity(tableName, null).intValue(); 551 if (quantity > availableIds.size()) 552 { 553 try 554 { 555 // Second parameter is false because we don't 556 // want the quantity to be adjusted for thread 557 // calls. 558 storeIDs(tableName, false, null); 559 if (log.isDebugEnabled()) 560 { 561 log.debug("Retrieved more ids for table: " + tableName); 562 } 563 } 564 catch (Exception exc) 565 { 566 log.error("There was a problem getting new IDs " 567 + "for table: " + tableName, exc); 568 } 569 } 570 } 571 } 572 log.debug("IDBroker thread finished."); 573 } 574 575 /*** 576 * Shuts down the IDBroker thread. 577 * 578 * Calling this method stops the thread that was started for this 579 * instance of the IDBroker. This method should be called during 580 * MapBroker Service shutdown. 581 */ 582 public void stop() 583 { 584 houseKeeperThread = null; 585 } 586 587 /*** 588 * Check the frequency of retrieving new ids from the database. 589 * If the frequency is high then we increase the amount (i.e. 590 * quantity column) of ids retrieved on each access. Tries to 591 * alter number of keys grabbed so that IDBroker retrieves a new 592 * set of ID's prior to their being needed. 593 * 594 * @param tableName The name of the table for which we want an id. 595 */ 596 private void checkTiming(String tableName) 597 { 598 // Check if quantity changing is switched on. 599 // If prefetch is turned off, changing quantity does not make sense 600 if (!configuration.getBoolean(DB_IDBROKER_CLEVERQUANTITY, true) 601 || !configuration.getBoolean(DB_IDBROKER_PREFETCH, true)) 602 { 603 return; 604 } 605 606 // Get the last id request for this table. 607 java.util.Date lastTime = (java.util.Date) lastQueryTime.get(tableName); 608 java.util.Date now = new java.util.Date(); 609 610 if (lastTime != null) 611 { 612 long thenLong = lastTime.getTime(); 613 long nowLong = now.getTime(); 614 int timeLapse = (int) (nowLong - thenLong); 615 if (timeLapse < SLEEP_PERIOD && timeLapse > 0) 616 { 617 if (log.isDebugEnabled()) 618 { 619 log.debug("Unscheduled retrieval of more ids for table: " 620 + tableName); 621 } 622 // Increase quantity, so that hopefully this does not 623 // happen again. 624 float rate = getQuantity(tableName, null).floatValue() 625 / (float) timeLapse; 626 quantityStore.put(tableName, new BigDecimal( 627 Math.ceil(SLEEP_PERIOD * rate * SAFETY_MARGIN))); 628 } 629 } 630 lastQueryTime.put(tableName, now); 631 } 632 633 /*** 634 * Grabs more ids from the id_table and stores it in the ids 635 * Hashtable. If adjustQuantity is set to true the amount of id's 636 * retrieved for each call to storeIDs will be adjusted. 637 * 638 * @param tableName The name of the table for which we want an id. 639 * @param adjustQuantity True if amount should be adjusted. 640 * @param connection a Connection 641 * @exception Exception a generic exception. 642 */ 643 private void storeIDs(String tableName, 644 boolean adjustQuantity, 645 Connection connection) 646 throws Exception 647 { 648 BigDecimal nextId = null; 649 BigDecimal quantity = null; 650 DatabaseMap dbMap = tableMap.getDatabaseMap(); 651 652 // Block on the table. Multiple tables are allowed to ask for 653 // ids simultaneously. 654 // TableMap tMap = dbMap.getTable(tableName); 655 // synchronized(tMap) see comment in the getNextIds method 656 // { 657 if (adjustQuantity) 658 { 659 checkTiming(tableName); 660 } 661 662 boolean useNewConnection = (connection == null) || (configuration 663 .getBoolean(DB_IDBROKER_USENEWCONNECTION, true)); 664 try 665 { 666 if (useNewConnection) 667 { 668 connection = Transaction.beginOptional(dbMap.getName(), 669 transactionsSupported); 670 } 671 672 // Write the current value of quantity of keys to grab 673 // to the database, primarily to obtain a write lock 674 // on the table/row, but this value will also be used 675 // as the starting value when an IDBroker is 676 // instantiated. 677 quantity = getQuantity(tableName, connection); 678 updateQuantity(connection, tableName, quantity); 679 680 // Read the next starting ID from the ID_TABLE. 681 BigDecimal[] results = selectRow(connection, tableName); 682 nextId = results[0]; // NEXT_ID column 683 684 // Update the row based on the quantity in the 685 // ID_TABLE. 686 BigDecimal newNextId = nextId.add(quantity); 687 updateNextId(connection, tableName, newNextId.toString()); 688 689 if (useNewConnection) 690 { 691 Transaction.commit(connection); 692 } 693 } 694 catch (Exception e) 695 { 696 if (useNewConnection) 697 { 698 Transaction.rollback(connection); 699 } 700 throw e; 701 } 702 703 List availableIds = (List) ids.get(tableName); 704 if (availableIds == null) 705 { 706 availableIds = new ArrayList(); 707 ids.put(tableName, availableIds); 708 } 709 710 // Create the ids and store them in the list of available ids. 711 int numId = quantity.intValue(); 712 for (int i = 0; i < numId; i++) 713 { 714 availableIds.add(nextId); 715 nextId = nextId.add(ONE); 716 } 717 // } 718 } 719 720 /*** 721 * This method allows you to get the number of ids that are to be 722 * cached in memory. This is either stored in quantityStore or 723 * read from the db. (ie the value in ID_TABLE.QUANTITY). 724 * 725 * Though this method returns a BigDecimal for the quantity, it is 726 * unlikey the system could withstand whatever conditions would lead 727 * to really needing a large quantity, it is retrieved as a BigDecimal 728 * only because it is going to be added to another BigDecimal. 729 * 730 * @param tableName The name of the table we want to query. 731 * @param connection a Connection 732 * @return An int with the number of ids cached in memory. 733 */ 734 private BigDecimal getQuantity(String tableName, Connection connection) 735 { 736 BigDecimal quantity = null; 737 738 // If prefetch is turned off we simply return 1 739 if (!configuration.getBoolean(DB_IDBROKER_PREFETCH, true)) 740 { 741 quantity = new BigDecimal(1); 742 } 743 // Initialize quantity, if necessary. 744 else if (quantityStore.containsKey(tableName)) 745 { 746 quantity = (BigDecimal) quantityStore.get(tableName); 747 } 748 else 749 { 750 Connection dbCon = null; 751 try 752 { 753 if (connection == null || configuration 754 .getBoolean(DB_IDBROKER_USENEWCONNECTION, true)) 755 { 756 String databaseName = tableMap.getDatabaseMap().getName(); 757 // Get a connection to the db 758 dbCon = Torque.getConnection(databaseName); 759 } 760 761 // Read the row from the ID_TABLE. 762 BigDecimal[] results = selectRow(dbCon, tableName); 763 764 // QUANTITY column. 765 quantity = results[1]; 766 quantityStore.put(tableName, quantity); 767 } 768 catch (Exception e) 769 { 770 quantity = new BigDecimal(10); 771 } 772 finally 773 { 774 // Return the connection to the pool. 775 try 776 { 777 dbCon.close(); 778 } 779 catch (Exception e) 780 { 781 log.error("Release of connection failed.", e); 782 } 783 } 784 } 785 return quantity; 786 } 787 788 /*** 789 * Helper method to select a row in the ID_TABLE. 790 * 791 * @param con A Connection. 792 * @param tableName The properly escaped name of the table to 793 * identify the row. 794 * @return A BigDecimal[]. 795 * @exception Exception a generic exception. 796 */ 797 private BigDecimal[] selectRow(Connection con, String tableName) 798 throws Exception 799 { 800 StringBuffer stmt = new StringBuffer(); 801 stmt.append("SELECT " + NEXT_ID + ", " + QUANTITY) 802 .append(" FROM " + ID_TABLE) 803 .append(" WHERE TABLE_NAME = '") 804 .append(tableName) 805 .append('\''); 806 807 Statement statement = null; 808 809 BigDecimal[] results = new BigDecimal[2]; 810 try 811 { 812 statement = con.createStatement(); 813 ResultSet rs = statement.executeQuery(stmt.toString()); 814 815 if (rs.next()) 816 { 817 // work around for MySQL which appears to support 818 // getBigDecimal in the source code, but the binary 819 // is throwing an NotImplemented exception. 820 results[0] = new BigDecimal(rs.getString(1)); // next_id 821 results[1] = new BigDecimal(rs.getString(2)); // quantity 822 } 823 else 824 { 825 throw new TorqueException("The table " + tableName 826 + " does not have a proper entry in the " + ID_TABLE); 827 } 828 } 829 finally 830 { 831 if (statement != null) 832 { 833 statement.close(); 834 } 835 } 836 837 return results; 838 } 839 840 /*** 841 * Helper method to update a row in the ID_TABLE. 842 * 843 * @param con A Connection. 844 * @param tableName The properly escaped name of the table to identify the 845 * row. 846 * @param id An int with the value to set for the id. 847 * @exception Exception Database error. 848 */ 849 private void updateNextId(Connection con, String tableName, String id) 850 throws Exception 851 { 852 853 854 StringBuffer stmt = new StringBuffer(id.length() 855 + tableName.length() + 50); 856 stmt.append("UPDATE " + ID_TABLE) 857 .append(" SET NEXT_ID = ") 858 .append(id) 859 .append(" WHERE TABLE_NAME = '") 860 .append(tableName) 861 .append('\''); 862 863 Statement statement = null; 864 865 if (log.isDebugEnabled()) 866 { 867 log.debug("updateNextId: " + stmt.toString()); 868 } 869 870 try 871 { 872 statement = con.createStatement(); 873 statement.executeUpdate(stmt.toString()); 874 } 875 finally 876 { 877 if (statement != null) 878 { 879 statement.close(); 880 } 881 } 882 } 883 884 /*** 885 * Helper method to update a row in the ID_TABLE. 886 * 887 * @param con A Connection. 888 * @param tableName The properly escaped name of the table to identify the 889 * row. 890 * @param quantity An int with the value of the quantity. 891 * @exception Exception Database error. 892 */ 893 private void updateQuantity(Connection con, String tableName, 894 BigDecimal quantity) 895 throws Exception 896 { 897 StringBuffer stmt = new StringBuffer(quantity.toString().length() 898 + tableName.length() + 50); 899 stmt.append("UPDATE " + ID_TABLE) 900 .append(" SET QUANTITY = ") 901 .append(quantity) 902 .append(" WHERE TABLE_NAME = '") 903 .append(tableName) 904 .append('\''); 905 906 Statement statement = null; 907 908 if (log.isDebugEnabled()) 909 { 910 log.debug("updateQuantity: " + stmt.toString()); 911 } 912 913 try 914 { 915 statement = con.createStatement(); 916 statement.executeUpdate(stmt.toString()); 917 } 918 finally 919 { 920 if (statement != null) 921 { 922 statement.close(); 923 } 924 } 925 } 926 }

This page was automatically generated by Maven