?? idbroker.java
字號:
package org.apache.torque.oid;/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License") * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */import java.math.BigDecimal;import java.sql.Connection;import java.sql.ResultSet;import java.sql.Statement;import java.util.ArrayList;import java.util.Hashtable;import java.util.Iterator;import java.util.List;import org.apache.commons.configuration.Configuration;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.torque.Torque;import org.apache.torque.TorqueException;import org.apache.torque.map.DatabaseMap;import org.apache.torque.map.TableMap;import org.apache.torque.util.Transaction;//!!// NOTE:// It would be nice to decouple this from// Torque. This is a great stand-alone utility./** * This method of ID generation is used to ensure that code is * more database independent. For example, MySQL has an auto-increment * feature while Oracle uses sequences. It caches several ids to * avoid needing a Connection for every request. * * This class uses the table ID_TABLE defined in * conf/master/id-table-schema.xml. The columns in ID_TABLE are used as * follows:<br> * * ID_TABLE_ID - The PK for this row (any unique int).<br> * TABLE_NAME - The name of the table you want ids for.<br> * NEXT_ID - The next id returned by IDBroker when it queries the * database (not when it returns an id from memory).<br> * QUANTITY - The number of ids that IDBroker will cache in memory.<br> * <p> * Use this class like this: * <pre> * int id = dbMap.getIDBroker().getNextIdAsInt(null, "TABLE_NAME"); * - or - * BigDecimal[] ids = ((IDBroker)dbMap.getIDBroker()) * .getNextIds("TABLE_NAME", numOfIdsToReturn); * </pre> * * NOTE: When the ID_TABLE must be updated we must ensure that * IDBroker objects running in different JVMs do not overwrite each * other. This is accomplished using using the transactional support * occuring in some databases. Using this class with a database that * does not support transactions should be limited to a single JVM. * * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a> * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a> * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> * @version $Id: IDBroker.java,v 1.31 2005/05/22 10:57:51 tfischer Exp $ */public class IDBroker implements Runnable, IdGenerator{ /** Name of the ID_TABLE = ID_TABLE */ public static final String ID_TABLE = "ID_TABLE"; /** Table_Name column name */ public static final String COL_TABLE_NAME = "TABLE_NAME"; /** Fully qualified Table_Name column name */ public static final String TABLE_NAME = ID_TABLE + "." + COL_TABLE_NAME; /** ID column name */ public static final String COL_TABLE_ID = "ID_TABLE_ID"; /** Fully qualified ID column name */ public static final String TABLE_ID = ID_TABLE + "." + COL_TABLE_ID; /** Next_ID column name */ public static final String COL_NEXT_ID = "NEXT_ID"; /** Fully qualified Next_ID column name */ public static final String NEXT_ID = ID_TABLE + "." + COL_NEXT_ID; /** Quantity column name */ public static final String COL_QUANTITY = "QUANTITY"; /** Fully qualified Quantity column name */ public static final String QUANTITY = ID_TABLE + "." + COL_QUANTITY; /** The TableMap referencing the ID_TABLE for this IDBroker. */ private TableMap tableMap; /** * The default size of the per-table meta data <code>Hashtable</code> * objects. */ private static final int DEFAULT_SIZE = 40; /** * The cached IDs for each table. * * Key: String table name. * Value: List of Integer IDs. */ private Hashtable ids = new Hashtable(DEFAULT_SIZE); /** * The quantity of ids to grab for each table. * * Key: String table name. * Value: Integer quantity. */ private Hashtable quantityStore = new Hashtable(DEFAULT_SIZE); /** * The last time this IDBroker queried the database for ids. * * Key: String table name. * Value: Date of last id request. */ private Hashtable lastQueryTime = new Hashtable(DEFAULT_SIZE); /** * Amount of time for the thread to sleep */ private static final int SLEEP_PERIOD = 1 * 60000; /** * The safety Margin */ private static final float SAFETY_MARGIN = 1.2f; /** * The houseKeeperThread thread */ private Thread houseKeeperThread = null; /** * Are transactions supported? */ private boolean transactionsSupported = false; /** * The value of ONE! */ private static final BigDecimal ONE = new BigDecimal("1"); /** the configuration */ private Configuration configuration; /** property name */ private static final String DB_IDBROKER_CLEVERQUANTITY = "idbroker.clever.quantity"; /** property name */ private static final String DB_IDBROKER_PREFETCH = "idbroker.prefetch"; /** property name */ private static final String DB_IDBROKER_USENEWCONNECTION = "idbroker.usenewconnection"; /** the log */ private Log log = LogFactory.getLog(IDBroker.class); /** * Creates an IDBroker for the ID table. * * @param tMap A TableMap. */ public IDBroker(TableMap tMap) { this.tableMap = tMap; configuration = Torque.getConfiguration(); // Start the housekeeper thread only if prefetch has not been disabled if (configuration.getBoolean(DB_IDBROKER_PREFETCH, true)) { houseKeeperThread = new Thread(this); // Indicate that this is a system thread. JVM will quit only when // there are no more active user threads. Settings threads spawned // internally by Torque as daemons allows commandline applications // using Torque terminate in an orderly manner. houseKeeperThread.setDaemon(true); houseKeeperThread.setName("Torque - ID Broker thread"); houseKeeperThread.start(); } // Check for Transaction support. Give warning message if // IDBroker is being used with a database that does not // support transactions. String dbName = tMap.getDatabaseMap().getName(); Connection dbCon = null; try { dbCon = Torque.getConnection(dbName); transactionsSupported = dbCon.getMetaData().supportsTransactions(); } catch (Exception e) { transactionsSupported = false; } finally { try { // Return the connection to the pool. dbCon.close(); } catch (Exception e) { } } if (!transactionsSupported) { log.warn("IDBroker is being used with db '" + dbName + "', which does not support transactions. IDBroker " + "attempts to use transactions to limit the possibility " + "of duplicate key generation. Without transactions, " + "duplicate key generation is possible if multiple JVMs " + "are used or other means are used to write to the " + "database."); } } /** * Set the configuration * * @param configuration the configuration */ public void setConfiguration(Configuration configuration) { this.configuration = configuration; } /** * Returns an id as a primitive int. Note this method does not * require a Connection, it just implements the KeyGenerator * interface. if a Connection is needed one will be requested. * To force the use of the passed in connection set the configuration * property torque.idbroker.usenewconnection = false * * @param connection A Connection. * @param tableName an Object that contains additional info. * @return An int with the value for the id. * @exception Exception Database error. */ public int getIdAsInt(Connection connection, Object tableName) throws Exception { return getIdAsBigDecimal(connection, tableName).intValue(); } /** * Returns an id as a primitive long. Note this method does not * require a Connection, it just implements the KeyGenerator * interface. if a Connection is needed one will be requested. * To force the use of the passed in connection set the configuration * property torque.idbroker.usenewconnection = false * * @param connection A Connection. * @param tableName a String that identifies a table. * @return A long with the value for the id. * @exception Exception Database error. */ public long getIdAsLong(Connection connection, Object tableName) throws Exception { return getIdAsBigDecimal(connection, tableName).longValue(); } /** * Returns an id as a BigDecimal. Note this method does not * require a Connection, it just implements the KeyGenerator * interface. if a Connection is needed one will be requested. * To force the use of the passed in connection set the configuration * property torque.idbroker.usenewconnection = false * * @param connection A Connection. * @param tableName a String that identifies a table.. * @return A BigDecimal id. * @exception Exception Database error. */ public BigDecimal getIdAsBigDecimal(Connection connection, Object tableName) throws Exception { BigDecimal[] id = getNextIds((String) tableName, 1, connection); return id[0]; } /** * Returns an id as a String. Note this method does not * require a Connection, it just implements the KeyGenerator * interface. if a Connection is needed one will be requested. * To force the use of the passed in connection set the configuration * property torque.idbroker.usenewconnection = false * * @param connection A Connection should be null. * @param tableName a String that identifies a table. * @return A String id * @exception Exception Database error. */ public String getIdAsString(Connection connection, Object tableName) throws Exception { return getIdAsBigDecimal(connection, tableName).toString(); } /** * A flag to determine the timing of the id generation * * @return a <code>boolean</code> value */ public boolean isPriorToInsert() { return true; } /** * A flag to determine the timing of the id generation * * @return a <code>boolean</code> value */ public boolean isPostInsert() { return false; } /** * A flag to determine whether a Connection is required to * generate an id. * * @return a <code>boolean</code> value */ public boolean isConnectionRequired() { return false; } /** * This method returns x number of ids for the given table. * * @param tableName The name of the table for which we want an id. * @param numOfIdsToReturn The desired number of ids. * @return A BigDecimal. * @exception Exception Database error. */ public synchronized BigDecimal[] getNextIds(String tableName, int numOfIdsToReturn) throws Exception { return getNextIds(tableName, numOfIdsToReturn, null); } /** * This method returns x number of ids for the given table. * Note this method does not require a Connection. * If a Connection is needed one will be requested. * To force the use of the passed in connection set the configuration * property torque.idbroker.usenewconnection = false * * @param tableName The name of the table for which we want an id. * @param numOfIdsToReturn The desired number of ids. * @param connection A Connection. * @return A BigDecimal. * @exception Exception Database error. */ public synchronized BigDecimal[] getNextIds(String tableName, int numOfIdsToReturn, Connection connection) throws Exception { if (tableName == null) { throw new Exception("getNextIds(): tableName == null"); } // A note about the synchronization: I (jmcnally) looked at // the synchronized blocks to avoid thread issues that were // being used in this and the storeId method. I do not think // they were being effective, so I synchronized the method. // I have left the blocks that did exist commented in the code // to make it easier for others to take a look, because it // would be preferrable to avoid the synchronization on the // method List availableIds = (List) ids.get(tableName); if (availableIds == null || availableIds.size() < numOfIdsToReturn) { if (availableIds == null) { log.debug("Forced id retrieval - no available list"); } else { log.debug("Forced id retrieval - " + availableIds.size()); } storeIDs(tableName, true, connection); availableIds = (List) ids.get(tableName); } int size = availableIds.size() < numOfIdsToReturn ? availableIds.size() : numOfIdsToReturn; BigDecimal[] results = new BigDecimal[size]; // We assume that availableIds will always come from the ids // Hashtable and would therefore always be the same object for // a specific table. // synchronized (availableIds) // { for (int i = size - 1; i >= 0; i--) { results[i] = (BigDecimal) availableIds.get(i); availableIds.remove(i); } // } return results; } /** * Describe <code>exists</code> method here. * * @param tableName a <code>String</code> value that is used to identify * the row * @return a <code>boolean</code> value * @exception TorqueException if an error occurs * @exception Exception a generic exception. */ public boolean exists(String tableName) throws TorqueException, Exception { String query = new StringBuffer(100) .append("select ") .append(TABLE_NAME)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -