Source Code Cross Referenced for IDBroker.java in  » Database-ORM » Torque » org » apache » torque » oid » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » Torque » org.apache.torque.oid 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package org.apache.torque.oid;
002:
003:        /*
004:         * Licensed to the Apache Software Foundation (ASF) under one
005:         * or more contributor license agreements.  See the NOTICE file
006:         * distributed with this work for additional information
007:         * regarding copyright ownership.  The ASF licenses this file
008:         * to you under the Apache License, Version 2.0 (the
009:         * "License"); you may not use this file except in compliance
010:         * with the License.  You may obtain a copy of the License at
011:         *
012:         *   http://www.apache.org/licenses/LICENSE-2.0
013:         *
014:         * Unless required by applicable law or agreed to in writing,
015:         * software distributed under the License is distributed on an
016:         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017:         * KIND, either express or implied.  See the License for the
018:         * specific language governing permissions and limitations
019:         * under the License.
020:         */
021:
022:        import java.math.BigDecimal;
023:        import java.sql.Connection;
024:        import java.sql.ResultSet;
025:        import java.sql.Statement;
026:        import java.util.ArrayList;
027:        import java.util.Hashtable;
028:        import java.util.Iterator;
029:        import java.util.List;
030:
031:        import org.apache.commons.configuration.Configuration;
032:        import org.apache.commons.logging.Log;
033:        import org.apache.commons.logging.LogFactory;
034:        import org.apache.torque.Database;
035:        import org.apache.torque.Torque;
036:        import org.apache.torque.TorqueException;
037:        import org.apache.torque.map.TableMap;
038:        import org.apache.torque.util.Transaction;
039:
040:        //!!
041:        // NOTE:
042:        // It would be nice to decouple this from
043:        // Torque. This is a great stand-alone utility.
044:
045:        /**
046:         * This method of ID generation is used to ensure that code is
047:         * more database independent.  For example, MySQL has an auto-increment
048:         * feature while Oracle uses sequences.  It caches several ids to
049:         * avoid needing a Connection for every request.
050:         *
051:         * This class uses the table ID_TABLE defined in
052:         * conf/master/id-table-schema.xml.  The columns in ID_TABLE are used as
053:         * follows:<br>
054:         *
055:         * ID_TABLE_ID - The PK for this row (any unique int).<br>
056:         * TABLE_NAME - The name of the table you want ids for.<br>
057:         * NEXT_ID - The next id returned by IDBroker when it queries the
058:         *           database (not when it returns an id from memory).<br>
059:         * QUANTITY - The number of ids that IDBroker will cache in memory.<br>
060:         * <p>
061:         * Use this class like this:
062:         * <pre>
063:         * int id = dbMap.getIDBroker().getNextIdAsInt(null, "TABLE_NAME");
064:         *  - or -
065:         * BigDecimal[] ids = ((IDBroker)dbMap.getIDBroker())
066:         *     .getNextIds("TABLE_NAME", numOfIdsToReturn);
067:         * </pre>
068:         *
069:         * NOTE: When the ID_TABLE must be updated we must ensure that
070:         * IDBroker objects running in different JVMs do not overwrite each
071:         * other.  This is accomplished using using the transactional support
072:         * occuring in some databases.  Using this class with a database that
073:         * does not support transactions should be limited to a single JVM.
074:         *
075:         * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
076:         * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
077:         * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
078:         * @version $Id: IDBroker.java 552333 2007-07-01 16:24:19Z tv $
079:         */
080:        public class IDBroker implements  Runnable, IdGenerator {
081:            /** Name of the ID_TABLE = ID_TABLE */
082:            public static final String ID_TABLE = "ID_TABLE";
083:
084:            /** Table_Name column name */
085:            public static final String COL_TABLE_NAME = "TABLE_NAME";
086:
087:            /** Fully qualified Table_Name column name */
088:            public static final String TABLE_NAME = ID_TABLE + "."
089:                    + COL_TABLE_NAME;
090:
091:            /** ID column name */
092:            public static final String COL_TABLE_ID = "ID_TABLE_ID";
093:
094:            /** Fully qualified ID column name */
095:            public static final String TABLE_ID = ID_TABLE + "." + COL_TABLE_ID;
096:
097:            /** Next_ID column name */
098:            public static final String COL_NEXT_ID = "NEXT_ID";
099:
100:            /** Fully qualified Next_ID column name */
101:            public static final String NEXT_ID = ID_TABLE + "." + COL_NEXT_ID;
102:
103:            /** Quantity column name */
104:            public static final String COL_QUANTITY = "QUANTITY";
105:
106:            /** Fully qualified Quantity column name */
107:            public static final String QUANTITY = ID_TABLE + "." + COL_QUANTITY;
108:
109:            /** the name of the database in which this IdBroker is running. */
110:            private String 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 ids = new Hashtable(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 quantityStore = new Hashtable(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 lastQueryTime = new Hashtable(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 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 ONE = new BigDecimal("1");
166:
167:            /** the configuration */
168:            private Configuration configuration;
169:
170:            /** property name */
171:            private static final String DB_IDBROKER_CLEVERQUANTITY = "idbroker.clever.quantity";
172:
173:            /** property name */
174:            private static final String DB_IDBROKER_PREFETCH = "idbroker.prefetch";
175:
176:            /** property name */
177:            private static final String DB_IDBROKER_USENEWCONNECTION = "idbroker.usenewconnection";
178:
179:            /** the log */
180:            private Log log = LogFactory.getLog(IDBroker.class);
181:
182:            /**
183:             * constructs an IdBroker for the given Database.
184:             * @param database the database where this IdBroker is running in.
185:             */
186:            public IDBroker(Database database) {
187:                this (database.getName());
188:            }
189:
190:            /**
191:             * Creates an IDBroker for the ID table.
192:             *
193:             * @param tMap A TableMap.
194:             * @deprecated Use IDBroker(DatabaseInfo) instead. Will be removed
195:             *             in a future version of Torque.
196:             */
197:            public IDBroker(TableMap tMap) {
198:                this (tMap.getDatabaseMap().getName());
199:            }
200:
201:            /**
202:             * Constructor.
203:             * Provided as long as both Constructors, IDBroker(DatabaseInfo) and
204:             * IDBroker(TableMap), are around.
205:             * @param databaseName the name of the database for which this IdBroker
206:             *        provides Ids.
207:             */
208:            private IDBroker(String databaseName) {
209:                this .databaseName = databaseName;
210:                configuration = Torque.getConfiguration();
211:
212:                // Start the housekeeper thread only if prefetch has not been disabled
213:                if (configuration.getBoolean(DB_IDBROKER_PREFETCH, true)) {
214:                    houseKeeperThread = new Thread(this );
215:                    // Indicate that this is a system thread. JVM will quit only when
216:                    // there are no more active user threads. Settings threads spawned
217:                    // internally by Torque as daemons allows commandline applications
218:                    // using Torque terminate in an orderly manner.
219:                    houseKeeperThread.setDaemon(true);
220:                    houseKeeperThread.setName("Torque - ID Broker thread");
221:                    houseKeeperThread.start();
222:                }
223:
224:                // Check for Transaction support.  Give warning message if
225:                // IDBroker is being used with a database that does not
226:                // support transactions.
227:                Connection dbCon = null;
228:                try {
229:                    dbCon = Torque.getConnection(databaseName);
230:                } catch (Throwable t) {
231:                    log.error("Could not open a connection to the database "
232:                            + databaseName, t);
233:                    transactionsSupported = false;
234:                }
235:                try {
236:                    transactionsSupported = dbCon.getMetaData()
237:                            .supportsTransactions();
238:                } catch (Exception e) {
239:                    log
240:                            .warn(
241:                                    "Could not read from connection Metadata"
242:                                            + " whether transactions are supported for the database "
243:                                            + databaseName, e);
244:                    transactionsSupported = false;
245:                } finally {
246:                    try {
247:                        // Return the connection to the pool.
248:                        dbCon.close();
249:                    } catch (Exception e) {
250:                        log
251:                                .warn(
252:                                        "Could not close the connection which was used "
253:                                                + "for testing whether transactions are supported",
254:                                        e);
255:                    }
256:                }
257:                if (!transactionsSupported) {
258:                    log
259:                            .warn("IDBroker is being used with db '"
260:                                    + databaseName
261:                                    + "', which does not support transactions. IDBroker "
262:                                    + "attempts to use transactions to limit the possibility "
263:                                    + "of duplicate key generation.  Without transactions, "
264:                                    + "duplicate key generation is possible if multiple JVMs "
265:                                    + "are used or other means are used to write to the "
266:                                    + "database.");
267:                }
268:            }
269:
270:            /**
271:             * Set the configuration
272:             *
273:             * @param configuration the configuration
274:             */
275:            public void setConfiguration(Configuration configuration) {
276:                this .configuration = configuration;
277:            }
278:
279:            /**
280:             * Returns an id as a primitive int.  Note this method does not
281:             * require a Connection, it just implements the KeyGenerator
282:             * interface.  if a Connection is needed one will be requested.
283:             * To force the use of the passed in connection set the configuration
284:             * property torque.idbroker.usenewconnection = false
285:             *
286:             * @param connection A Connection.
287:             * @param tableName an Object that contains additional info.
288:             * @return An int with the value for the id.
289:             * @exception Exception Database error.
290:             */
291:            public int getIdAsInt(Connection connection, Object tableName)
292:                    throws Exception {
293:                return getIdAsBigDecimal(connection, tableName).intValue();
294:            }
295:
296:            /**
297:             * Returns an id as a primitive long. Note this method does not
298:             * require a Connection, it just implements the KeyGenerator
299:             * interface.  if a Connection is needed one will be requested.
300:             * To force the use of the passed in connection set the configuration
301:             * property torque.idbroker.usenewconnection = false
302:             *
303:             * @param connection A Connection.
304:             * @param tableName a String that identifies a table.
305:             * @return A long with the value for the id.
306:             * @exception Exception Database error.
307:             */
308:            public long getIdAsLong(Connection connection, Object tableName)
309:                    throws Exception {
310:                return getIdAsBigDecimal(connection, tableName).longValue();
311:            }
312:
313:            /**
314:             * Returns an id as a BigDecimal. 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 BigDecimal id.
323:             * @exception Exception Database error.
324:             */
325:            public BigDecimal getIdAsBigDecimal(Connection connection,
326:                    Object tableName) throws Exception {
327:                BigDecimal[] id = getNextIds((String) tableName, 1, connection);
328:                return id[0];
329:            }
330:
331:            /**
332:             * Returns an id as a String. 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 should be null.
339:             * @param tableName a String that identifies a table.
340:             * @return A String id
341:             * @exception Exception Database error.
342:             */
343:            public String getIdAsString(Connection connection, Object tableName)
344:                    throws Exception {
345:                return getIdAsBigDecimal(connection, tableName).toString();
346:            }
347:
348:            /**
349:             * A flag to determine the timing of the id generation     *
350:             * @return a <code>boolean</code> value
351:             */
352:            public boolean isPriorToInsert() {
353:                return true;
354:            }
355:
356:            /**
357:             * A flag to determine the timing of the id generation
358:             *
359:             * @return a <code>boolean</code> value
360:             */
361:            public boolean isPostInsert() {
362:                return false;
363:            }
364:
365:            /**
366:             * A flag to determine whether a Connection is required to
367:             * generate an id.
368:             *
369:             * @return a <code>boolean</code> value
370:             */
371:            public boolean isConnectionRequired() {
372:                return false;
373:            }
374:
375:            /**
376:             * This method returns x number of ids for the given table.
377:             *
378:             * @param tableName The name of the table for which we want an id.
379:             * @param numOfIdsToReturn The desired number of ids.
380:             * @return A BigDecimal.
381:             * @exception Exception Database error.
382:             */
383:            public synchronized BigDecimal[] getNextIds(String tableName,
384:                    int numOfIdsToReturn) throws Exception {
385:                return getNextIds(tableName, numOfIdsToReturn, null);
386:            }
387:
388:            /**
389:             * This method returns x number of ids for the given table.
390:             * Note this method does not require a Connection.
391:             * If a Connection is needed one will be requested.
392:             * To force the use of the passed in connection set the configuration
393:             * property torque.idbroker.usenewconnection = false
394:             *
395:             * @param tableName The name of the table for which we want an id.
396:             * @param numOfIdsToReturn The desired number of ids.
397:             * @param connection A Connection.
398:             * @return A BigDecimal.
399:             * @exception Exception Database error.
400:             */
401:            public synchronized BigDecimal[] getNextIds(String tableName,
402:                    int numOfIdsToReturn, Connection connection)
403:                    throws Exception {
404:                if (tableName == null) {
405:                    throw new Exception("getNextIds(): tableName == null");
406:                }
407:
408:                // A note about the synchronization:  I (jmcnally) looked at
409:                // the synchronized blocks to avoid thread issues that were
410:                // being used in this and the storeId method.  I do not think
411:                // they were being effective, so I synchronized the method.
412:                // I have left the blocks that did exist commented in the code
413:                // to make it easier for others to take a look, because it
414:                // would be preferrable to avoid the synchronization on the
415:                // method
416:
417:                List availableIds = (List) ids.get(tableName);
418:
419:                if (availableIds == null
420:                        || availableIds.size() < numOfIdsToReturn) {
421:                    if (availableIds == null) {
422:                        log.debug("Forced id retrieval - no available list");
423:                    } else {
424:                        log.debug("Forced id retrieval - "
425:                                + availableIds.size());
426:                    }
427:                    storeIDs(tableName, true, connection);
428:                    availableIds = (List) ids.get(tableName);
429:                }
430:
431:                int size = availableIds.size() < numOfIdsToReturn ? availableIds
432:                        .size()
433:                        : numOfIdsToReturn;
434:
435:                BigDecimal[] results = new BigDecimal[size];
436:
437:                // We assume that availableIds will always come from the ids
438:                // Hashtable and would therefore always be the same object for
439:                // a specific table.
440:                //        synchronized (availableIds)
441:                //        {
442:                for (int i = size - 1; i >= 0; i--) {
443:                    results[i] = (BigDecimal) availableIds.get(i);
444:                    availableIds.remove(i);
445:                }
446:                //        }
447:
448:                return results;
449:            }
450:
451:            /**
452:             * @param tableName a <code>String</code> value that is used to identify
453:             * the row
454:             * @return a <code>boolean</code> value
455:             * @exception TorqueException if a Torque error occurs.
456:             * @exception Exception if another error occurs.
457:             */
458:            public boolean exists(String tableName) throws Exception {
459:                String query = new StringBuffer(100).append("select ").append(
460:                        TABLE_NAME).append(" where ").append(TABLE_NAME)
461:                        .append("='").append(tableName).append('\'').toString();
462:
463:                boolean exists = false;
464:                Connection dbCon = null;
465:                try {
466:                    dbCon = Torque.getConnection(databaseName);
467:                    Statement statement = dbCon.createStatement();
468:                    ResultSet rs = statement.executeQuery(query);
469:                    exists = rs.next();
470:                    statement.close();
471:                } finally {
472:                    // Return the connection to the pool.
473:                    try {
474:                        dbCon.close();
475:                    } catch (Exception e) {
476:                        log.error("Release of connection failed.", e);
477:                    }
478:                }
479:                return exists;
480:            }
481:
482:            /**
483:             * A background thread that tries to ensure that when someone asks
484:             * for ids, that there are already some loaded and that the
485:             * database is not accessed.
486:             */
487:            public void run() {
488:                log.debug("IDBroker thread was started.");
489:
490:                Thread this Thread = Thread.currentThread();
491:                while (houseKeeperThread == this Thread) {
492:                    try {
493:                        Thread.sleep(SLEEP_PERIOD);
494:                    } catch (InterruptedException exc) {
495:                        // ignored
496:                    }
497:
498:                    // logger.info("IDBroker thread checking for more keys.");
499:                    Iterator it = ids.keySet().iterator();
500:                    while (it.hasNext()) {
501:                        String tableName = (String) it.next();
502:                        if (log.isDebugEnabled()) {
503:                            log.debug("IDBroker thread checking for more keys "
504:                                    + "on table: " + tableName);
505:                        }
506:                        List availableIds = (List) ids.get(tableName);
507:                        int quantity = getQuantity(tableName, null).intValue();
508:                        if (quantity > availableIds.size()) {
509:                            try {
510:                                // Second parameter is false because we don't
511:                                // want the quantity to be adjusted for thread
512:                                // calls.
513:                                storeIDs(tableName, false, null);
514:                                if (log.isDebugEnabled()) {
515:                                    log.debug("Retrieved more ids for table: "
516:                                            + tableName);
517:                                }
518:                            } catch (Exception exc) {
519:                                log.error(
520:                                        "There was a problem getting new IDs "
521:                                                + "for table: " + tableName,
522:                                        exc);
523:                            }
524:                        }
525:                    }
526:                }
527:                log.debug("IDBroker thread finished.");
528:            }
529:
530:            /**
531:             * Shuts down the IDBroker thread.
532:             *
533:             * Calling this method stops the thread that was started for this
534:             * instance of the IDBroker. This method should be called during
535:             * MapBroker Service shutdown.
536:             */
537:            public void stop() {
538:                houseKeeperThread = null;
539:            }
540:
541:            /**
542:             * Check the frequency of retrieving new ids from the database.
543:             * If the frequency is high then we increase the amount (i.e.
544:             * quantity column) of ids retrieved on each access.  Tries to
545:             * alter number of keys grabbed so that IDBroker retrieves a new
546:             * set of ID's prior to their being needed.
547:             *
548:             * @param tableName The name of the table for which we want an id.
549:             */
550:            private void checkTiming(String tableName) {
551:                // Check if quantity changing is switched on.
552:                // If prefetch is turned off, changing quantity does not make sense
553:                if (!configuration.getBoolean(DB_IDBROKER_CLEVERQUANTITY, true)
554:                        || !configuration
555:                                .getBoolean(DB_IDBROKER_PREFETCH, true)) {
556:                    return;
557:                }
558:
559:                // Get the last id request for this table.
560:                java.util.Date lastTime = (java.util.Date) lastQueryTime
561:                        .get(tableName);
562:                java.util.Date now = new java.util.Date();
563:
564:                if (lastTime != null) {
565:                    long thenLong = lastTime.getTime();
566:                    long nowLong = now.getTime();
567:                    int timeLapse = (int) (nowLong - thenLong);
568:                    if (timeLapse < SLEEP_PERIOD && timeLapse > 0) {
569:                        if (log.isDebugEnabled()) {
570:                            log
571:                                    .debug("Unscheduled retrieval of more ids for table: "
572:                                            + tableName);
573:                        }
574:                        // Increase quantity, so that hopefully this does not
575:                        // happen again.
576:                        float rate = getQuantity(tableName, null).floatValue()
577:                                / (float) timeLapse;
578:                        quantityStore.put(tableName, new BigDecimal(Math
579:                                .ceil(SLEEP_PERIOD * rate * SAFETY_MARGIN)));
580:                    }
581:                }
582:                lastQueryTime.put(tableName, now);
583:            }
584:
585:            /**
586:             * Grabs more ids from the id_table and stores it in the ids
587:             * Hashtable.  If adjustQuantity is set to true the amount of id's
588:             * retrieved for each call to storeIDs will be adjusted.
589:             *
590:             * @param tableName The name of the table for which we want an id.
591:             * @param adjustQuantity True if amount should be adjusted.
592:             * @param connection a Connection
593:             * @exception Exception a generic exception.
594:             */
595:            private synchronized void storeIDs(String tableName,
596:                    boolean adjustQuantity, Connection connection)
597:                    throws Exception {
598:                BigDecimal nextId = null;
599:                BigDecimal quantity = null;
600:
601:                // Block on the table.  Multiple tables are allowed to ask for
602:                // ids simultaneously.
603:                //        TableMap tMap = dbMap.getTable(tableName);
604:                //        synchronized(tMap)  see comment in the getNextIds method
605:                //        {
606:                if (adjustQuantity) {
607:                    checkTiming(tableName);
608:                }
609:
610:                boolean useNewConnection = (connection == null)
611:                        || (configuration.getBoolean(
612:                                DB_IDBROKER_USENEWCONNECTION, true));
613:                try {
614:                    if (useNewConnection) {
615:                        connection = Transaction.beginOptional(databaseName,
616:                                transactionsSupported);
617:                    }
618:
619:                    // Write the current value of quantity of keys to grab
620:                    // to the database, primarily to obtain a write lock
621:                    // on the table/row, but this value will also be used
622:                    // as the starting value when an IDBroker is
623:                    // instantiated.
624:                    quantity = getQuantity(tableName, connection);
625:                    updateQuantity(connection, tableName, quantity);
626:
627:                    // Read the next starting ID from the ID_TABLE.
628:                    BigDecimal[] results = selectRow(connection, tableName);
629:                    nextId = results[0]; // NEXT_ID column
630:
631:                    // Update the row based on the quantity in the
632:                    // ID_TABLE.
633:                    BigDecimal newNextId = nextId.add(quantity);
634:                    updateNextId(connection, tableName, newNextId.toString());
635:
636:                    if (useNewConnection) {
637:                        Transaction.commit(connection);
638:                    }
639:                } catch (Exception e) {
640:                    if (useNewConnection) {
641:                        Transaction.rollback(connection);
642:                    }
643:                    throw e;
644:                }
645:
646:                List availableIds = (List) ids.get(tableName);
647:                if (availableIds == null) {
648:                    availableIds = new ArrayList();
649:                    ids.put(tableName, availableIds);
650:                }
651:
652:                // Create the ids and store them in the list of available ids.
653:                int numId = quantity.intValue();
654:                for (int i = 0; i < numId; i++) {
655:                    availableIds.add(nextId);
656:                    nextId = nextId.add(ONE);
657:                }
658:                //        }
659:            }
660:
661:            /**
662:             * This method allows you to get the number of ids that are to be
663:             * cached in memory.  This is either stored in quantityStore or
664:             * read from the db. (ie the value in ID_TABLE.QUANTITY).
665:             *
666:             * Though this method returns a BigDecimal for the quantity, it is
667:             * unlikey the system could withstand whatever conditions would lead
668:             * to really needing a large quantity, it is retrieved as a BigDecimal
669:             * only because it is going to be added to another BigDecimal.
670:             *
671:             * @param tableName The name of the table we want to query.
672:             * @param connection a Connection
673:             * @return An int with the number of ids cached in memory.
674:             */
675:            private BigDecimal getQuantity(String tableName,
676:                    Connection connection) {
677:                BigDecimal quantity = null;
678:
679:                // If prefetch is turned off we simply return 1
680:                if (!configuration.getBoolean(DB_IDBROKER_PREFETCH, true)) {
681:                    quantity = new BigDecimal((double) 1);
682:                }
683:                // Initialize quantity, if necessary.
684:                else if (quantityStore.containsKey(tableName)) {
685:                    quantity = (BigDecimal) quantityStore.get(tableName);
686:                } else {
687:                    Connection dbCon = null;
688:                    try {
689:                        if (connection == null
690:                                || configuration.getBoolean(
691:                                        DB_IDBROKER_USENEWCONNECTION, true)) {
692:                            // Get a connection to the db
693:                            dbCon = Torque.getConnection(databaseName);
694:                        }
695:
696:                        // Read the row from the ID_TABLE.
697:                        BigDecimal[] results = selectRow(dbCon, tableName);
698:
699:                        // QUANTITY column.
700:                        quantity = results[1];
701:                        quantityStore.put(tableName, quantity);
702:                    } catch (Exception e) {
703:                        quantity = new BigDecimal((double) 10);
704:                    } finally {
705:                        // Return the connection to the pool.
706:                        try {
707:                            dbCon.close();
708:                        } catch (Exception e) {
709:                            log.error("Release of connection failed.", e);
710:                        }
711:                    }
712:                }
713:                return quantity;
714:            }
715:
716:            /**
717:             * Helper method to select a row in the ID_TABLE.
718:             *
719:             * @param con A Connection.
720:             * @param tableName The properly escaped name of the table to
721:             * identify the row.
722:             * @return A BigDecimal[].
723:             * @exception Exception a generic exception.
724:             */
725:            private BigDecimal[] selectRow(Connection con, String tableName)
726:                    throws Exception {
727:                StringBuffer stmt = new StringBuffer();
728:                stmt.append("SELECT ").append(COL_NEXT_ID).append(", ").append(
729:                        COL_QUANTITY).append(" FROM ").append(ID_TABLE).append(
730:                        " WHERE ").append(COL_TABLE_NAME).append(" = '")
731:                        .append(tableName).append('\'');
732:
733:                Statement statement = null;
734:
735:                BigDecimal[] results = new BigDecimal[2];
736:                try {
737:                    statement = con.createStatement();
738:                    ResultSet rs = statement.executeQuery(stmt.toString());
739:
740:                    if (rs.next()) {
741:                        // work around for MySQL which appears to support
742:                        // getBigDecimal in the source code, but the binary
743:                        // is throwing an NotImplemented exception.
744:                        results[0] = new BigDecimal(rs.getString(1)); // next_id
745:                        results[1] = new BigDecimal(rs.getString(2)); // quantity
746:                    } else {
747:                        throw new TorqueException("The table " + tableName
748:                                + " does not have a proper entry in the "
749:                                + ID_TABLE);
750:                    }
751:                } finally {
752:                    if (statement != null) {
753:                        statement.close();
754:                    }
755:                }
756:
757:                return results;
758:            }
759:
760:            /**
761:             * Helper method to update a row in the ID_TABLE.
762:             *
763:             * @param con A Connection.
764:             * @param tableName The properly escaped name of the table to identify the
765:             * row.
766:             * @param id An int with the value to set for the id.
767:             * @exception Exception Database error.
768:             */
769:            private void updateNextId(Connection con, String tableName,
770:                    String id) throws Exception {
771:
772:                StringBuffer stmt = new StringBuffer(id.length()
773:                        + tableName.length() + 50);
774:                stmt.append("UPDATE " + ID_TABLE).append(" SET ").append(
775:                        COL_NEXT_ID).append(" = ").append(id).append(" WHERE ")
776:                        .append(COL_TABLE_NAME).append(" = '")
777:                        .append(tableName).append('\'');
778:
779:                Statement statement = null;
780:
781:                if (log.isDebugEnabled()) {
782:                    log.debug("updateNextId: " + stmt.toString());
783:                }
784:
785:                try {
786:                    statement = con.createStatement();
787:                    statement.executeUpdate(stmt.toString());
788:                } finally {
789:                    if (statement != null) {
790:                        statement.close();
791:                    }
792:                }
793:            }
794:
795:            /**
796:             * Helper method to update a row in the ID_TABLE.
797:             *
798:             * @param con A Connection.
799:             * @param tableName The properly escaped name of the table to identify the
800:             * row.
801:             * @param quantity An int with the value of the quantity.
802:             * @exception Exception Database error.
803:             */
804:            private void updateQuantity(Connection con, String tableName,
805:                    BigDecimal quantity) throws Exception {
806:                StringBuffer stmt = new StringBuffer(quantity.toString()
807:                        .length()
808:                        + tableName.length() + 50);
809:                stmt.append("UPDATE ").append(ID_TABLE).append(" SET ").append(
810:                        COL_QUANTITY).append(" = ").append(quantity).append(
811:                        " WHERE ").append(COL_TABLE_NAME).append(" = '")
812:                        .append(tableName).append('\'');
813:
814:                Statement statement = null;
815:
816:                if (log.isDebugEnabled()) {
817:                    log.debug("updateQuantity: " + stmt.toString());
818:                }
819:
820:                try {
821:                    statement = con.createStatement();
822:                    statement.executeUpdate(stmt.toString());
823:                } finally {
824:                    if (statement != null) {
825:                        statement.close();
826:                    }
827:                }
828:            }
829:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.