/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.util.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.hyperic.util.ConfigPropertyException; public class IDGenerator { private int itsDBType = DBUtil.DATABASE_UNKNOWN; private String itsSequenceName; private int itsSequenceInterval; private String itsTableName = null; private String itsAlterSQL = null; private String itsSelectSQL = null; private long itsLastKey = 1; private long itsMaxKey = 1; // logging context private String ctx = null; private boolean isInitialized = false; private DBUtil dbUtil; /** * This constructor is for use inside an entity bean. * * @param ctx The logging context to use * @param theSequenceName name of the database sequence * @param theSequenceInterval how many values we should grab at a time from * the db * @param theDSName the name of the data source used to connect to the db */ public IDGenerator(String ctx, String theSequenceName, int theSequenceInterval, DBUtil dbUtil) { this.ctx = ctx; itsSequenceName = theSequenceName; itsSequenceInterval = theSequenceInterval; itsTableName = getTableName(itsSequenceName); isInitialized = false; this.dbUtil = dbUtil; } public synchronized long getNewID() throws ConfigPropertyException, SequenceRetrievalException, SQLException { if (!isInitialized) init(); if (itsLastKey >= itsMaxKey) getBatch(); return ++itsLastKey; } private synchronized void getBatch() throws SequenceRetrievalException, SQLException { // Go to database and set new values for itsLastKey and itsMaxKey Connection conn = null; PreparedStatement selectPS = null; ResultSet rs = null; try { conn = getConnection(); selectPS = conn.prepareStatement(itsSelectSQL); rs = selectPS.executeQuery(); if (rs != null && rs.next()) { itsLastKey = rs.getLong(1) - 1; itsMaxKey = itsLastKey + itsSequenceInterval; } else { throw new SequenceRetrievalException( "IDGenerator.getBatch: sequence failed to return a value: " + itsSequenceName); } doAlterSequence(conn); } finally { DBUtil.closeJDBCObjects(ctx, conn, selectPS, rs); } } private void doAlterSequence(Connection conn) throws SQLException { PreparedStatement alterPS = null; ResultSet rs = null; try { switch (itsDBType) { case DBUtil.DATABASE_POSTGRESQL_7: case DBUtil.DATABASE_POSTGRESQL_8: case DBUtil.DATABASE_POSTGRESQL_9: itsAlterSQL = "SELECT setval ('" + itsSequenceName + "', " + itsMaxKey + ")"; alterPS = conn.prepareStatement(itsAlterSQL); rs = alterPS.executeQuery(); break; case DBUtil.DATABASE_ORACLE_8: case DBUtil.DATABASE_ORACLE_9: case DBUtil.DATABASE_ORACLE_10: case DBUtil.DATABASE_ORACLE_11: alterPS = conn.prepareStatement(itsAlterSQL); alterPS.executeUpdate(); break; } } finally { DBUtil.closeResultSet(ctx, rs); DBUtil.closeStatement(ctx, alterPS); } } private synchronized void init() throws ConfigPropertyException, SQLException { if (isInitialized) { return; } isInitialized = true; Connection conn = null; try { conn = getConnection(); itsDBType = DBUtil.getDBType(conn); } finally { DBUtil.closeConnection(ctx, conn); } switch (itsDBType) { case DBUtil.DATABASE_POSTGRESQL_7: case DBUtil.DATABASE_POSTGRESQL_8: case DBUtil.DATABASE_POSTGRESQL_9: itsAlterSQL = "SELECT setval ('" + itsSequenceName + "', " + itsMaxKey + ")"; itsSelectSQL = "SELECT nextval('" + itsSequenceName + "'::text)"; break; case DBUtil.DATABASE_ORACLE_8: case DBUtil.DATABASE_ORACLE_9: case DBUtil.DATABASE_ORACLE_10: itsAlterSQL = "ALTER SEQUENCE " + itsSequenceName + " INCREMENT BY " + itsSequenceInterval; itsSelectSQL = "SELECT " + itsSequenceName + ".nextval from DUAL"; break; case DBUtil.DATABASE_MYSQL5: itsAlterSQL = "ALTER TABLE " + itsTableName + " AUTO_INCREMENT = " + itsMaxKey; // mysql assumes all columns with autoincrement are name ID itsSelectSQL = "SELECT MAX(ID) + 1 FROM " + itsTableName; break; } } /** * Get the name of the table a sequence refers to assumes sequence names * will follow the pattern SOME_TABLE_NAME_KEYCOL_SEQ * @return SOME_TABLE_NAME */ private static String getTableName(String sequence) { String[] tokens = sequence.split("_"); String sub = new String(); int i = 0; while (i < (tokens.length - 2)) { sub += tokens[i] + (i != (tokens.length - 3) ? "_" : ""); i++; } return sub; } private Connection getConnection() throws SQLException { return dbUtil.getConnection(); } }