KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > torque > oid > IDBroker


1 package org.apache.torque.oid;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements. See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership. The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License. You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied. See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */

21
22 import java.math.BigDecimal JavaDoc;
23 import java.sql.Connection JavaDoc;
24 import java.sql.ResultSet JavaDoc;
25 import java.sql.Statement JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Hashtable JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30
31 import org.apache.commons.configuration.Configuration;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.torque.Database;
35 import org.apache.torque.Torque;
36 import org.apache.torque.TorqueException;
37 import org.apache.torque.map.TableMap;
38 import org.apache.torque.util.Transaction;
39
40 //!!
41
// NOTE:
42
// It would be nice to decouple this from
43
// Torque. This is a great stand-alone utility.
44

45 /**
46  * This method of ID generation is used to ensure that code is
47  * more database independent. For example, MySQL has an auto-increment
48  * feature while Oracle uses sequences. It caches several ids to
49  * avoid needing a Connection for every request.
50  *
51  * This class uses the table ID_TABLE defined in
52  * conf/master/id-table-schema.xml. The columns in ID_TABLE are used as
53  * follows:<br>
54  *
55  * ID_TABLE_ID - The PK for this row (any unique int).<br>
56  * TABLE_NAME - The name of the table you want ids for.<br>
57  * NEXT_ID - The next id returned by IDBroker when it queries the
58  * database (not when it returns an id from memory).<br>
59  * QUANTITY - The number of ids that IDBroker will cache in memory.<br>
60  * <p>
61  * Use this class like this:
62  * <pre>
63  * int id = dbMap.getIDBroker().getNextIdAsInt(null, "TABLE_NAME");
64  * - or -
65  * BigDecimal[] ids = ((IDBroker)dbMap.getIDBroker())
66  * .getNextIds("TABLE_NAME", numOfIdsToReturn);
67  * </pre>
68  *
69  * NOTE: When the ID_TABLE must be updated we must ensure that
70  * IDBroker objects running in different JVMs do not overwrite each
71  * other. This is accomplished using using the transactional support
72  * occuring in some databases. Using this class with a database that
73  * does not support transactions should be limited to a single JVM.
74  *
75  * @author <a HREF="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
76  * @author <a HREF="mailto:jmcnally@collab.net">John D. McNally</a>
77  * @author <a HREF="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
78  * @version $Id: IDBroker.java 476550 2006-11-18 16:08:37Z tfischer $
79  */

80 public class IDBroker implements Runnable JavaDoc, IdGenerator
81 {
82     /** Name of the ID_TABLE = ID_TABLE */
83     public static final String JavaDoc ID_TABLE = "ID_TABLE";
84
85     /** Table_Name column name */
86     public static final String JavaDoc COL_TABLE_NAME = "TABLE_NAME";
87
88     /** Fully qualified Table_Name column name */
89     public static final String JavaDoc TABLE_NAME = ID_TABLE + "." + COL_TABLE_NAME;
90
91     /** ID column name */
92     public static final String JavaDoc COL_TABLE_ID = "ID_TABLE_ID";
93
94     /** Fully qualified ID column name */
95     public static final String JavaDoc TABLE_ID = ID_TABLE + "." + COL_TABLE_ID;
96
97     /** Next_ID column name */
98     public static final String JavaDoc COL_NEXT_ID = "NEXT_ID";
99
100     /** Fully qualified Next_ID column name */
101     public static final String JavaDoc NEXT_ID = ID_TABLE + "." + COL_NEXT_ID;
102
103     /** Quantity column name */
104     public static final String JavaDoc COL_QUANTITY = "QUANTITY";
105
106     /** Fully qualified Quantity column name */
107     public static final String JavaDoc QUANTITY = ID_TABLE + "." + COL_QUANTITY;
108
109     /** the name of the database in which this IdBroker is running. */
110     private String JavaDoc databaseName;
111
112     /**
113      * The default size of the per-table meta data <code>Hashtable</code>
114      * objects.
115      */

116     private static final int DEFAULT_SIZE = 40;
117
118     /**
119      * The cached IDs for each table.
120      *
121      * Key: String table name.
122      * Value: List of Integer IDs.
123      */

124     private Hashtable JavaDoc ids = new Hashtable JavaDoc(DEFAULT_SIZE);
125
126     /**
127      * The quantity of ids to grab for each table.
128      *
129      * Key: String table name.
130      * Value: Integer quantity.
131      */

132     private Hashtable JavaDoc quantityStore = new Hashtable JavaDoc(DEFAULT_SIZE);
133
134     /**
135      * The last time this IDBroker queried the database for ids.
136      *
137      * Key: String table name.
138      * Value: Date of last id request.
139      */

140     private Hashtable JavaDoc lastQueryTime = new Hashtable JavaDoc(DEFAULT_SIZE);
141
142     /**
143      * Amount of time for the thread to sleep
144      */

145     private static final int SLEEP_PERIOD = 60000;
146
147     /**
148      * The safety Margin
149      */

150     private static final float SAFETY_MARGIN = 1.2f;
151
152     /**
153      * The houseKeeperThread thread
154      */

155     private Thread JavaDoc houseKeeperThread = null;
156
157     /**
158      * Are transactions supported?
159      */

160     private boolean transactionsSupported = false;
161
162     /**
163      * The value of ONE!
164      */

165     private static final BigDecimal JavaDoc ONE = new BigDecimal JavaDoc("1");
166
167     /** the configuration */
168     private Configuration configuration;
169
170     /** property name */
171     private static final String JavaDoc DB_IDBROKER_CLEVERQUANTITY =
172         "idbroker.clever.quantity";
173
174     /** property name */
175     private static final String JavaDoc DB_IDBROKER_PREFETCH =
176         "idbroker.prefetch";
177
178     /** property name */
179     private static final String JavaDoc DB_IDBROKER_USENEWCONNECTION =
180         "idbroker.usenewconnection";
181
182     /** the log */
183     private Log log = LogFactory.getLog(IDBroker.class);
184
185     /**
186      * constructs an IdBroker for the given Database.
187      * @param database the database where this IdBroker is running in.
188      */

189     public IDBroker(Database database)
190     {
191         this(database.getName());
192     }
193
194     /**
195      * Creates an IDBroker for the ID table.
196      *
197      * @param tMap A TableMap.
198      * @deprecated Use IDBroker(DatabaseInfo) instead. Will be removed
199      * in a future version of Torque.
200      */

201     public IDBroker(TableMap tMap)
202     {
203         this(tMap.getDatabaseMap().getName());
204     }
205
206     /**
207      * Constructor.
208      * Provided as long as both Constructors, IDBroker(DatabaseInfo) and
209      * IDBroker(TableMap), are around.
210      * @param databaseName the name of the database for which this IdBroker
211      * provides Ids.
212      */

213     private IDBroker(String JavaDoc databaseName)
214     {
215         this.databaseName = databaseName;
216         configuration = Torque.getConfiguration();
217
218         // Start the housekeeper thread only if prefetch has not been disabled
219
if (configuration.getBoolean(DB_IDBROKER_PREFETCH, true))
220         {
221             houseKeeperThread = new Thread JavaDoc(this);
222             // Indicate that this is a system thread. JVM will quit only when
223
// there are no more active user threads. Settings threads spawned
224
// internally by Torque as daemons allows commandline applications
225
// using Torque terminate in an orderly manner.
226
houseKeeperThread.setDaemon(true);
227             houseKeeperThread.setName("Torque - ID Broker thread");
228             houseKeeperThread.start();
229         }
230
231         // Check for Transaction support. Give warning message if
232
// IDBroker is being used with a database that does not
233
// support transactions.
234
Connection JavaDoc dbCon = null;
235         try
236         {
237             dbCon = Torque.getConnection(databaseName);
238         }
239         catch (Throwable JavaDoc t)
240         {
241             log.error("Could not open a connection to the database "
242                     + databaseName,
243                     t);
244             transactionsSupported = false;
245         }
246         try
247         {
248             transactionsSupported = dbCon.getMetaData().supportsTransactions();
249         }
250         catch (Exception JavaDoc e)
251         {
252             log.warn("Could not read from connection Metadata"
253                     + " whether transactions are supported for the database "
254                     + databaseName,
255                     e);
256             transactionsSupported = false;
257         }
258         finally
259         {
260             try
261             {
262                 // Return the connection to the pool.
263
dbCon.close();
264             }
265             catch (Exception JavaDoc e)
266             {
267                 log.warn("Could not close the connection which was used "
268                         + "for testing whether transactions are supported",
269                         e);
270             }
271         }
272         if (!transactionsSupported)
273         {
274             log.warn("IDBroker is being used with db '" + databaseName
275                     + "', which does not support transactions. IDBroker "
276                     + "attempts to use transactions to limit the possibility "
277                     + "of duplicate key generation. Without transactions, "
278                     + "duplicate key generation is possible if multiple JVMs "
279                     + "are used or other means are used to write to the "
280                     + "database.");
281         }
282     }
283
284     /**
285      * Set the configuration
286      *
287      * @param configuration the configuration
288      */

289     public void setConfiguration(Configuration configuration)
290     {
291         this.configuration = configuration;
292     }
293
294     /**
295      * Returns an id as a primitive int. Note this method does not
296      * require a Connection, it just implements the KeyGenerator
297      * interface. if a Connection is needed one will be requested.
298      * To force the use of the passed in connection set the configuration
299      * property torque.idbroker.usenewconnection = false
300      *
301      * @param connection A Connection.
302      * @param tableName an Object that contains additional info.
303      * @return An int with the value for the id.
304      * @exception Exception Database error.
305      */

306     public int getIdAsInt(Connection JavaDoc connection, Object JavaDoc tableName)
307         throws Exception JavaDoc
308     {
309         return getIdAsBigDecimal(connection, tableName).intValue();
310     }
311
312
313     /**
314      * Returns an id as a primitive long. Note this method does not
315      * require a Connection, it just implements the KeyGenerator
316      * interface. if a Connection is needed one will be requested.
317      * To force the use of the passed in connection set the configuration
318      * property torque.idbroker.usenewconnection = false
319      *
320      * @param connection A Connection.
321      * @param tableName a String that identifies a table.
322      * @return A long with the value for the id.
323      * @exception Exception Database error.
324      */

325     public long getIdAsLong(Connection JavaDoc connection, Object JavaDoc tableName)
326         throws Exception JavaDoc
327     {
328         return getIdAsBigDecimal(connection, tableName).longValue();
329     }
330
331     /**
332      * Returns an id as a BigDecimal. Note this method does not
333      * require a Connection, it just implements the KeyGenerator
334      * interface. if a Connection is needed one will be requested.
335      * To force the use of the passed in connection set the configuration
336      * property torque.idbroker.usenewconnection = false
337      *
338      * @param connection A Connection.
339      * @param tableName a String that identifies a table..
340      * @return A BigDecimal id.
341      * @exception Exception Database error.
342      */

343     public BigDecimal JavaDoc getIdAsBigDecimal(Connection JavaDoc connection,
344                                         Object JavaDoc tableName)
345         throws Exception JavaDoc
346     {
347         BigDecimal JavaDoc[] id = getNextIds((String JavaDoc) tableName, 1, connection);
348         return id[0];
349     }
350
351     /**
352      * Returns an id as a String. Note this method does not
353      * require a Connection, it just implements the KeyGenerator
354      * interface. if a Connection is needed one will be requested.
355      * To force the use of the passed in connection set the configuration
356      * property torque.idbroker.usenewconnection = false
357      *
358      * @param connection A Connection should be null.
359      * @param tableName a String that identifies a table.
360      * @return A String id
361      * @exception Exception Database error.
362      */

363     public String JavaDoc getIdAsString(Connection JavaDoc connection, Object JavaDoc tableName)
364         throws Exception JavaDoc
365     {
366         return getIdAsBigDecimal(connection, tableName).toString();
367     }
368
369
370     /**
371      * A flag to determine the timing of the id generation *
372      * @return a <code>boolean</code> value
373      */

374     public boolean isPriorToInsert()
375     {
376         return true;
377     }
378
379     /**
380      * A flag to determine the timing of the id generation
381      *
382      * @return a <code>boolean</code> value
383      */

384     public boolean isPostInsert()
385     {
386         return false;
387     }
388
389     /**
390      * A flag to determine whether a Connection is required to
391      * generate an id.
392      *
393      * @return a <code>boolean</code> value
394      */

395     public boolean isConnectionRequired()
396     {
397         return false;
398     }
399
400     /**
401      * This method returns x number of ids for the given table.
402      *
403      * @param tableName The name of the table for which we want an id.
404      * @param numOfIdsToReturn The desired number of ids.
405      * @return A BigDecimal.
406      * @exception Exception Database error.
407      */

408     public synchronized BigDecimal JavaDoc[] getNextIds(String JavaDoc tableName,
409                                                 int numOfIdsToReturn)
410         throws Exception JavaDoc
411     {
412         return getNextIds(tableName, numOfIdsToReturn, null);
413     }
414
415     /**
416      * This method returns x number of ids for the given table.
417      * Note this method does not require a Connection.
418      * If a Connection is needed one will be requested.
419      * To force the use of the passed in connection set the configuration
420      * property torque.idbroker.usenewconnection = false
421      *
422      * @param tableName The name of the table for which we want an id.
423      * @param numOfIdsToReturn The desired number of ids.
424      * @param connection A Connection.
425      * @return A BigDecimal.
426      * @exception Exception Database error.
427      */

428     public synchronized BigDecimal JavaDoc[] getNextIds(String JavaDoc tableName,
429                                                 int numOfIdsToReturn,
430                                                 Connection JavaDoc connection)
431         throws Exception JavaDoc
432     {
433         if (tableName == null)
434         {
435             throw new Exception JavaDoc("getNextIds(): tableName == null");
436         }
437
438         // A note about the synchronization: I (jmcnally) looked at
439
// the synchronized blocks to avoid thread issues that were
440
// being used in this and the storeId method. I do not think
441
// they were being effective, so I synchronized the method.
442
// I have left the blocks that did exist commented in the code
443
// to make it easier for others to take a look, because it
444
// would be preferrable to avoid the synchronization on the
445
// method
446

447         List JavaDoc availableIds = (List JavaDoc) ids.get(tableName);
448
449         if (availableIds == null || availableIds.size() < numOfIdsToReturn)
450         {
451             if (availableIds == null)
452             {
453                 log.debug("Forced id retrieval - no available list");
454             }
455             else
456             {
457                 log.debug("Forced id retrieval - " + availableIds.size());
458             }
459             storeIDs(tableName, true, connection);
460             availableIds = (List JavaDoc) ids.get(tableName);
461         }
462
463         int size = availableIds.size() < numOfIdsToReturn
464                 ? availableIds.size() : numOfIdsToReturn;
465
466         BigDecimal JavaDoc[] results = new BigDecimal JavaDoc[size];
467
468         // We assume that availableIds will always come from the ids
469
// Hashtable and would therefore always be the same object for
470
// a specific table.
471
// synchronized (availableIds)
472
// {
473
for (int i = size - 1; i >= 0; i--)
474         {
475             results[i] = (BigDecimal JavaDoc) availableIds.get(i);
476             availableIds.remove(i);
477         }
478         // }
479

480         return results;
481     }
482
483     /**
484      * @param tableName a <code>String</code> value that is used to identify
485      * the row
486      * @return a <code>boolean</code> value
487      * @exception TorqueException if a Torque error occurs.
488      * @exception Exception if another error occurs.
489      */

490     public boolean exists(String JavaDoc tableName)
491         throws Exception JavaDoc
492     {
493         String JavaDoc query = new StringBuffer JavaDoc(100)
494             .append("select ")
495             .append(TABLE_NAME)
496             .append(" where ")
497             .append(TABLE_NAME).append("='").append(tableName).append('\'')
498             .toString();
499
500         boolean exists = false;
501         Connection JavaDoc dbCon = null;
502         try
503         {
504             dbCon = Torque.getConnection(databaseName);
505             Statement JavaDoc statement = dbCon.createStatement();
506             ResultSet JavaDoc rs = statement.executeQuery(query);
507             exists = rs.next();
508             statement.close();
509         }
510         finally
511         {
512             // Return the connection to the pool.
513
try
514             {
515                 dbCon.close();
516             }
517             catch (Exception JavaDoc e)
518             {
519                 log.error("Release of connection failed.", e);
520             }
521         }
522         return exists;
523     }
524
525     /**
526      * A background thread that tries to ensure that when someone asks
527      * for ids, that there are already some loaded and that the
528      * database is not accessed.
529      */

530     public void run()
531     {
532         log.debug("IDBroker thread was started.");
533
534         Thread JavaDoc thisThread = Thread.currentThread();
535         while (houseKeeperThread == thisThread)
536         {
537             try
538             {
539                 Thread.sleep(SLEEP_PERIOD);
540             }
541             catch (InterruptedException JavaDoc exc)
542             {
543                 // ignored
544
}
545
546             // logger.info("IDBroker thread checking for more keys.");
547
Iterator JavaDoc it = ids.keySet().iterator();
548             while (it.hasNext())
549             {
550                 String JavaDoc tableName = (String JavaDoc) it.next();
551                 if (log.isDebugEnabled())
552                 {
553                     log.debug("IDBroker thread checking for more keys "
554                             + "on table: " + tableName);
555                 }
556                 List JavaDoc availableIds = (List JavaDoc) ids.get(tableName);
557                 int quantity = getQuantity(tableName, null).intValue();
558                 if (quantity > availableIds.size())
559                 {
560                     try
561                     {
562                         // Second parameter is false because we don't
563
// want the quantity to be adjusted for thread
564
// calls.
565
storeIDs(tableName, false, null);
566                         if (log.isDebugEnabled())
567                         {
568                             log.debug("Retrieved more ids for table: " + tableName);
569                         }
570                     }
571                     catch (Exception JavaDoc exc)
572                     {
573                         log.error("There was a problem getting new IDs "
574                                      + "for table: " + tableName, exc);
575                     }
576                 }
577             }
578         }
579         log.debug("IDBroker thread finished.");
580     }
581
582     /**
583      * Shuts down the IDBroker thread.
584      *
585      * Calling this method stops the thread that was started for this
586      * instance of the IDBroker. This method should be called during
587      * MapBroker Service shutdown.
588      */

589     public void stop()
590     {
591         houseKeeperThread = null;
592     }
593
594     /**
595      * Check the frequency of retrieving new ids from the database.
596      * If the frequency is high then we increase the amount (i.e.
597      * quantity column) of ids retrieved on each access. Tries to
598      * alter number of keys grabbed so that IDBroker retrieves a new
599      * set of ID's prior to their being needed.
600      *
601      * @param tableName The name of the table for which we want an id.
602      */

603     private void checkTiming(String JavaDoc tableName)
604     {
605         // Check if quantity changing is switched on.
606
// If prefetch is turned off, changing quantity does not make sense
607
if (!configuration.getBoolean(DB_IDBROKER_CLEVERQUANTITY, true)
608             || !configuration.getBoolean(DB_IDBROKER_PREFETCH, true))
609         {
610             return;
611         }
612
613         // Get the last id request for this table.
614
java.util.Date JavaDoc lastTime = (java.util.Date JavaDoc) lastQueryTime.get(tableName);
615         java.util.Date JavaDoc now = new java.util.Date JavaDoc();
616
617         if (lastTime != null)
618         {
619             long thenLong = lastTime.getTime();
620             long nowLong = now.getTime();
621             int timeLapse = (int) (nowLong - thenLong);
622             if (timeLapse < SLEEP_PERIOD && timeLapse > 0)
623             {
624                 if (log.isDebugEnabled())
625                 {
626                     log.debug("Unscheduled retrieval of more ids for table: "
627                             + tableName);
628                 }
629                 // Increase quantity, so that hopefully this does not
630
// happen again.
631
float rate = getQuantity(tableName, null).floatValue()
632                     / (float) timeLapse;
633                 quantityStore.put(tableName, new BigDecimal JavaDoc(
634                     Math.ceil(SLEEP_PERIOD * rate * SAFETY_MARGIN)));
635             }
636         }
637         lastQueryTime.put(tableName, now);
638     }
639
640     /**
641      * Grabs more ids from the id_table and stores it in the ids
642      * Hashtable. If adjustQuantity is set to true the amount of id's
643      * retrieved for each call to storeIDs will be adjusted.
644      *
645      * @param tableName The name of the table for which we want an id.
646      * @param adjustQuantity True if amount should be adjusted.
647      * @param connection a Connection
648      * @exception Exception a generic exception.
649      */

650     private synchronized void storeIDs(String JavaDoc tableName,
651                           boolean adjustQuantity,
652                           Connection JavaDoc connection)
653         throws Exception JavaDoc
654     {
655         BigDecimal JavaDoc nextId = null;
656         BigDecimal JavaDoc quantity = null;
657
658         // Block on the table. Multiple tables are allowed to ask for
659
// ids simultaneously.
660
// TableMap tMap = dbMap.getTable(tableName);
661
// synchronized(tMap) see comment in the getNextIds method
662
// {
663
if (adjustQuantity)
664         {
665             checkTiming(tableName);
666         }
667
668         boolean useNewConnection = (connection == null) || (configuration
669                 .getBoolean(DB_IDBROKER_USENEWCONNECTION, true));
670         try
671         {
672             if (useNewConnection)
673             {
674                 connection = Transaction.beginOptional(databaseName,
675                     transactionsSupported);
676             }
677
678             // Write the current value of quantity of keys to grab
679
// to the database, primarily to obtain a write lock
680
// on the table/row, but this value will also be used
681
// as the starting value when an IDBroker is
682
// instantiated.
683
quantity = getQuantity(tableName, connection);
684             updateQuantity(connection, tableName, quantity);
685
686             // Read the next starting ID from the ID_TABLE.
687
BigDecimal JavaDoc[] results = selectRow(connection, tableName);
688             nextId = results[0]; // NEXT_ID column
689

690             // Update the row based on the quantity in the
691
// ID_TABLE.
692
BigDecimal JavaDoc newNextId = nextId.add(quantity);
693             updateNextId(connection, tableName, newNextId.toString());
694
695             if (useNewConnection)
696             {
697                 Transaction.commit(connection);
698             }
699         }
700         catch (Exception JavaDoc e)
701         {
702             if (useNewConnection)
703             {
704                 Transaction.rollback(connection);
705             }
706             throw e;
707         }
708
709         List JavaDoc availableIds = (List JavaDoc) ids.get(tableName);
710         if (availableIds == null)
711         {
712             availableIds = new ArrayList JavaDoc();
713             ids.put(tableName, availableIds);
714         }
715
716         // Create the ids and store them in the list of available ids.
717
int numId = quantity.intValue();
718         for (int i = 0; i < numId; i++)
719         {
720             availableIds.add(nextId);
721             nextId = nextId.add(ONE);
722         }
723         // }
724
}
725
726     /**
727      * This method allows you to get the number of ids that are to be
728      * cached in memory. This is either stored in quantityStore or
729      * read from the db. (ie the value in ID_TABLE.QUANTITY).
730      *
731      * Though this method returns a BigDecimal for the quantity, it is
732      * unlikey the system could withstand whatever conditions would lead
733      * to really needing a large quantity, it is retrieved as a BigDecimal
734      * only because it is going to be added to another BigDecimal.
735      *
736      * @param tableName The name of the table we want to query.
737      * @param connection a Connection
738      * @return An int with the number of ids cached in memory.
739      */

740     private BigDecimal JavaDoc getQuantity(String JavaDoc tableName, Connection JavaDoc connection)
741     {
742         BigDecimal JavaDoc quantity = null;
743
744         // If prefetch is turned off we simply return 1
745
if (!configuration.getBoolean(DB_IDBROKER_PREFETCH, true))
746         {
747             quantity = new BigDecimal JavaDoc(1);
748         }
749         // Initialize quantity, if necessary.
750
else if (quantityStore.containsKey(tableName))
751         {
752             quantity = (BigDecimal JavaDoc) quantityStore.get(tableName);
753         }
754         else
755         {
756             Connection JavaDoc dbCon = null;
757             try
758             {
759                 if (connection == null || configuration
760                     .getBoolean(DB_IDBROKER_USENEWCONNECTION, true))
761                 {
762                     // Get a connection to the db
763
dbCon = Torque.getConnection(databaseName);
764                 }
765
766                 // Read the row from the ID_TABLE.
767
BigDecimal JavaDoc[] results = selectRow(dbCon, tableName);
768
769                 // QUANTITY column.
770
quantity = results[1];
771                 quantityStore.put(tableName, quantity);
772             }
773             catch (Exception JavaDoc e)
774             {
775                 quantity = new BigDecimal JavaDoc(10);
776             }
777             finally
778             {
779                 // Return the connection to the pool.
780
try
781                 {
782                     dbCon.close();
783                 }
784                 catch (Exception JavaDoc e)
785                 {
786                     log.error("Release of connection failed.", e);
787                 }
788             }
789         }
790         return quantity;
791     }
792
793     /**
794      * Helper method to select a row in the ID_TABLE.
795      *
796      * @param con A Connection.
797      * @param tableName The properly escaped name of the table to
798      * identify the row.
799      * @return A BigDecimal[].
800      * @exception Exception a generic exception.
801      */

802     private BigDecimal JavaDoc[] selectRow(Connection JavaDoc con, String JavaDoc tableName)
803         throws Exception JavaDoc
804     {
805         StringBuffer JavaDoc stmt = new StringBuffer JavaDoc();
806         stmt.append("SELECT ")
807             .append(COL_NEXT_ID)
808             .append(", ")
809             .append(COL_QUANTITY)
810             .append(" FROM ")
811             .append(ID_TABLE)
812             .append(" WHERE ")
813             .append(COL_TABLE_NAME)
814             .append(" = '")
815             .append(tableName)
816             .append('\'');
817
818         Statement JavaDoc statement = null;
819
820         BigDecimal JavaDoc[] results = new BigDecimal JavaDoc[2];
821         try
822         {
823             statement = con.createStatement();
824             ResultSet JavaDoc rs = statement.executeQuery(stmt.toString());
825
826             if (rs.next())
827             {
828                 // work around for MySQL which appears to support
829
// getBigDecimal in the source code, but the binary
830
// is throwing an NotImplemented exception.
831
results[0] = new BigDecimal JavaDoc(rs.getString(1)); // next_id
832
results[1] = new BigDecimal JavaDoc(rs.getString(2)); // quantity
833
}
834             else
835             {
836                 throw new TorqueException("The table " + tableName
837                         + " does not have a proper entry in the " + ID_TABLE);
838             }
839         }
840         finally
841         {
842             if (statement != null)
843             {
844                 statement.close();
845             }
846         }
847
848         return results;
849     }
850
851     /**
852      * Helper method to update a row in the ID_TABLE.
853      *
854      * @param con A Connection.
855      * @param tableName The properly escaped name of the table to identify the
856      * row.
857      * @param id An int with the value to set for the id.
858      * @exception Exception Database error.
859      */

860     private void updateNextId(Connection JavaDoc con, String JavaDoc tableName, String JavaDoc id)
861         throws Exception JavaDoc
862     {
863
864
865         StringBuffer JavaDoc stmt = new StringBuffer JavaDoc(id.length()
866                                              + tableName.length() + 50);
867         stmt.append("UPDATE " + ID_TABLE)
868             .append(" SET ")
869             .append(COL_NEXT_ID)
870             .append(" = ")
871             .append(id)
872             .append(" WHERE ")
873             .append(COL_TABLE_NAME)
874             .append(" = '")
875             .append(tableName)
876             .append('\'');
877
878         Statement JavaDoc statement = null;
879
880         if (log.isDebugEnabled())
881         {
882             log.debug("updateNextId: " + stmt.toString());
883         }
884
885         try
886         {
887             statement = con.createStatement();
888             statement.executeUpdate(stmt.toString());
889         }
890         finally
891         {
892             if (statement != null)
893             {
894                 statement.close();
895             }
896         }
897     }
898
899     /**
900      * Helper method to update a row in the ID_TABLE.
901      *
902      * @param con A Connection.
903      * @param tableName The properly escaped name of the table to identify the
904      * row.
905      * @param quantity An int with the value of the quantity.
906      * @exception Exception Database error.
907      */

908     private void updateQuantity(Connection JavaDoc con, String JavaDoc tableName,
909                                 BigDecimal JavaDoc quantity)
910         throws Exception JavaDoc
911     {
912         StringBuffer JavaDoc stmt = new StringBuffer JavaDoc(quantity.toString().length()
913                                              + tableName.length() + 50);
914         stmt.append("UPDATE ")
915             .append(ID_TABLE)
916             .append(" SET ")
917             .append(COL_QUANTITY)
918             .append(" = ")
919             .append(quantity)
920             .append(" WHERE ")
921             .append(COL_TABLE_NAME)
922             .append(" = '")
923             .append(tableName)
924             .append('\'');
925
926         Statement JavaDoc statement = null;
927
928         if (log.isDebugEnabled())
929         {
930             log.debug("updateQuantity: " + stmt.toString());
931         }
932
933         try
934         {
935             statement = con.createStatement();
936             statement.executeUpdate(stmt.toString());
937         }
938         finally
939         {
940             if (statement != null)
941             {
942                 statement.close();
943             }
944         }
945     }
946 }
947
Popular Tags