一、前言
关于topcoder源码包id_generator.jar中的com.topcoder.util.idgenerator.IDGenerator、com.topcoder.util.idgenerator.IDGeneratorImpl、com.topcoder.util.idgenerator.OracleSequenceGenerator定义接口及实现类,通过对表id_sequences的FOR UPDATE锁的方式进行产生有效序列,详情参见源码说明部分。
二、源码说明
1.IDGenerator接口
package com.topcoder.util.idgenerator;@b@@b@import java.math.BigInteger;@b@@b@public abstract interface IDGenerator@b@{@b@ public abstract String getIDName();@b@@b@ public abstract long getNextID()@b@ throws IDGenerationException;@b@@b@ public abstract BigInteger getNextBigID()@b@ throws IDGenerationException;@b@}
2.IDGeneratorImpl实现类
package com.topcoder.util.idgenerator;@b@@b@import com.topcoder.db.connectionfactory.DBConnectionFactory;@b@import com.topcoder.db.connectionfactory.DBConnectionFactoryImpl;@b@import com.topcoder.db.connectionfactory.UnknownConnectionException;@b@import java.math.BigInteger;@b@import java.sql.Connection;@b@import java.sql.PreparedStatement;@b@import java.sql.ResultSet;@b@import java.sql.SQLException;@b@@b@public class IDGeneratorImpl@b@ implements IDGenerator@b@{@b@ private static final String DBFACTORY_NAMESPACE = "com.topcoder.db.connectionfactory.DBConnectionFactoryImpl";@b@ private static final String CONNECTION_NAME = "DefaultSequence";@b@ private static final String NEXT_BLOCK_START = "next_block_start";@b@ private static final String BLOCK_SIZE = "block_size";@b@ private static final String EXHAUSTED = "exhausted";@b@ private static final String SELECT_NEXT_BLOCK = "SELECT next_block_start, block_size, exhausted FROM id_sequences WHERE name = ? FOR UPDATE";@b@ private static final String UPDATE_NEXT_BLOCK_START = "UPDATE id_sequences SET next_block_start = ? WHERE name = ?";@b@ private static final String UPDATE_EXHAUSTED = "UPDATE id_sequences SET exhausted = 1 WHERE name = ?";@b@ private final String idName;@b@ private long nextID;@b@ private int idsLeft = 0;@b@ private DBConnectionFactory factory = null;@b@@b@ public IDGeneratorImpl(String idName)@b@ throws IDGenerationException@b@ {@b@ if (idName == null) {@b@ throw new NoSuchIDSequenceException("The specified IDName is null");@b@ }@b@@b@ this.idName = idName;@b@@b@ ResultSet rs = null;@b@ Connection connection = null;@b@ PreparedStatement selectStmt = null;@b@ try@b@ {@b@ connection = getConnection();@b@ selectStmt = connection.prepareStatement("SELECT next_block_start, block_size, exhausted FROM id_sequences WHERE name = ? FOR UPDATE");@b@ selectStmt.setString(1, idName);@b@@b@ rs = selectStmt.executeQuery();@b@@b@ if (!(rs.next()))@b@ throw new NoSuchIDSequenceException("The specified IDName does not exist in the underlying persistence.");@b@ }@b@ catch (SQLException e)@b@ {@b@ }@b@ finally {@b@ IDGeneratorHelper.closeResultSet(rs);@b@ IDGeneratorHelper.closeStatement(selectStmt);@b@ IDGeneratorHelper.close(connection);@b@ }@b@ }@b@@b@ private Connection getConnection()@b@ throws IDGenerationException@b@ {@b@ Connection connection = null;@b@ try@b@ {@b@ if (this.factory == null) {@b@ this.factory = new DBConnectionFactoryImpl("com.topcoder.db.connectionfactory.DBConnectionFactoryImpl");@b@ }@b@@b@ try@b@ {@b@ connection = this.factory.createConnection("DefaultSequence");@b@ } catch (UnknownConnectionException e) {@b@ connection = this.factory.createConnection();@b@ }@b@@b@ connection.setAutoCommit(false);@b@ connection.setTransactionIsolation(8);@b@ } catch (Exception e) {@b@ throw new IDGenerationException("Failed to get connection from db factory.", e);@b@ }@b@@b@ return connection;@b@ }@b@@b@ public String getIDName()@b@ {@b@ return this.idName;@b@ }@b@@b@ public synchronized long getNextID()@b@ throws IDGenerationException@b@ {@b@ if (this.idsLeft <= 0)@b@ {@b@ synchronized (IDGeneratorImpl.class) {@b@ getNextBlock();@b@ }@b@ }@b@@b@ this.idsLeft -= 1;@b@@b@ return (this.nextID++);@b@ }@b@@b@ public synchronized BigInteger getNextBigID()@b@ throws IDGenerationException@b@ {@b@ return BigInteger.valueOf(getNextID());@b@ }@b@@b@ private synchronized void getNextBlock()@b@ throws IDGenerationException@b@ {@b@ ResultSet rs = null;@b@ Connection connection = getConnection();@b@ PreparedStatement selectStmt = null;@b@ PreparedStatement updateExaustedStmt = null;@b@ PreparedStatement updateStartStmt = null;@b@ try@b@ {@b@ selectStmt = connection.prepareStatement("SELECT next_block_start, block_size, exhausted FROM id_sequences WHERE name = ? FOR UPDATE");@b@ selectStmt.setString(1, this.idName);@b@ rs = selectStmt.executeQuery();@b@@b@ if (!(rs.next())) {@b@ throw new NoSuchIDSequenceException("The specified IDName does not exist in the database.");@b@ }@b@@b@ if (rs.getBoolean("exhausted")) {@b@ throw new IDsExhaustedException("The ids of specified IDName are exausted yet.");@b@ }@b@@b@ long myNextID = rs.getLong("next_block_start");@b@ int blockSize = rs.getInt("block_size");@b@@b@ if (myNextID - 1L > 9223372036854775807L - blockSize) {@b@ throw new IDsExhaustedException("The ids left are not sufficient to make a block.");@b@ }@b@@b@ if (myNextID - 1L >= 9223372036854775807L - blockSize) {@b@ updateExaustedStmt = connection.prepareStatement("UPDATE id_sequences SET exhausted = 1 WHERE name = ?");@b@ updateExaustedStmt.setString(1, this.idName);@b@ updateExaustedStmt.executeUpdate();@b@ }@b@@b@ long myMaxBlockID = myNextID + blockSize - 1L;@b@@b@ updateStartStmt = connection.prepareStatement("UPDATE id_sequences SET next_block_start = ? WHERE name = ?");@b@ updateStartStmt.setLong(1, myMaxBlockID + 1L);@b@ updateStartStmt.setString(2, this.idName);@b@ updateStartStmt.executeUpdate();@b@@b@ commit(connection);@b@@b@ this.idsLeft = blockSize;@b@ this.nextID = myNextID;@b@ }@b@ catch (SQLException e)@b@ {@b@ throw new IDGenerationException("Failed to get next block.", e);@b@ } finally {@b@ IDGeneratorHelper.closeResultSet(rs);@b@ IDGeneratorHelper.closeStatement(selectStmt);@b@ IDGeneratorHelper.closeStatement(updateExaustedStmt);@b@ IDGeneratorHelper.closeStatement(updateStartStmt);@b@ IDGeneratorHelper.close(connection);@b@ }@b@ }@b@@b@ private void rollback(Connection connection)@b@ {@b@ try@b@ {@b@ connection.rollback();@b@ }@b@ catch (Exception e)@b@ {@b@ }@b@ }@b@@b@ private void commit(Connection connection)@b@ {@b@ try@b@ {@b@ connection.commit();@b@ }@b@ catch (Exception e)@b@ {@b@ }@b@ }@b@}
3.OracleSequenceGenerator的oracle实现类
package com.topcoder.util.idgenerator;@b@@b@import com.topcoder.db.connectionfactory.DBConnectionFactory;@b@import com.topcoder.db.connectionfactory.DBConnectionFactoryImpl;@b@import java.math.BigDecimal;@b@import java.math.BigInteger;@b@import java.sql.Connection;@b@import java.sql.PreparedStatement;@b@import java.sql.ResultSet;@b@import java.sql.SQLException;@b@import java.sql.Statement;@b@@b@public class OracleSequenceGenerator@b@ implements IDGenerator@b@{@b@ private static final String DBFACTORY_NAMESPACE = "com.topcoder.db.connectionfactory.DBConnectionFactoryImpl";@b@ private static final String CONNECTION_NAME = "OracleSequence";@b@ private static final String SELECT_BLOCK_SIZE = "SELECT INCREMENT_BY, MAX_VALUE FROM seq WHERE SEQUENCE_NAME = ?";@b@ private static String selectNextIdSql = null;@b@ private static final String DEFAULT_SEQ_SUFFFIX = "_SEQ";@b@ private String idName;@b@ private String seqName;@b@ private long nextID = 0L;@b@ private int blockSize = 0;@b@ private int idsLeft = 0;@b@ private long maxID;@b@ private DBConnectionFactory factory = null;@b@ private boolean sequenceExist = false;@b@@b@ public OracleSequenceGenerator(String idName)@b@ throws IDGenerationException@b@ {@b@ if (idName == null) {@b@ throw new NoSuchIDSequenceException("The given idName is null.");@b@ }@b@@b@ this.idName = idName;@b@ setSequenceName(idName + "_SEQ");@b@ }@b@@b@ public String getIDName()@b@ {@b@ return this.idName;@b@ }@b@@b@ public synchronized long getNextID()@b@ throws IDGenerationException@b@ {@b@ if (this.idsLeft <= 0)@b@ {@b@ synchronized (OracleSequenceGenerator.class) {@b@ getNextBlock();@b@ }@b@ }@b@@b@ this.idsLeft -= 1;@b@@b@ return (this.nextID++);@b@ }@b@@b@ private synchronized void getNextBlock()@b@ throws IDGenerationException@b@ {@b@ if ((!(this.sequenceExist)) && @b@ (!(setupSequence()))) {@b@ throw new NoSuchIDSequenceException("sequence: " + this.seqName + " does not exist.");@b@ }@b@@b@ Statement selectStmt = null;@b@ ResultSet rs = null;@b@ Connection connection = null;@b@ try@b@ {@b@ connection = getConnection();@b@@b@ selectStmt = connection.createStatement();@b@ rs = selectStmt.executeQuery(selectNextIdSql);@b@@b@ if (rs.next()) {@b@ this.nextID = rs.getLong(1);@b@ this.idsLeft = this.blockSize;@b@@b@ if (this.nextID + this.idsLeft - 1L > this.maxID) {@b@ throw new IDsExhaustedException("The ids left are not sufficient to make a block.");@b@ }@b@@b@ }@b@@b@ }@b@ catch (SQLException e)@b@ {@b@ throw new IDGenerationException("Failed to get next sequence id.", e);@b@ } finally {@b@ IDGeneratorHelper.closeStatement(selectStmt);@b@ IDGeneratorHelper.close(connection);@b@ }@b@ }@b@@b@ public synchronized BigInteger getNextBigID()@b@ throws IDGenerationException@b@ {@b@ return BigInteger.valueOf(getNextID());@b@ }@b@@b@ public synchronized String getSequenceName()@b@ {@b@ return this.seqName;@b@ }@b@@b@ public synchronized void setSequenceName(String seqName)@b@ {@b@ if (seqName == null) {@b@ throw new NullPointerException("The given seqName is null.");@b@ }@b@@b@ if (!(seqName.equals(this.seqName))) {@b@ this.seqName = seqName;@b@@b@ setupSequence();@b@ }@b@ }@b@@b@ private Connection getConnection()@b@ throws IDGenerationException@b@ {@b@ try@b@ {@b@ if (this.factory == null) {@b@ this.factory = new DBConnectionFactoryImpl("com.topcoder.db.connectionfactory.DBConnectionFactoryImpl");@b@ }@b@@b@ return this.factory.createConnection("OracleSequence");@b@ } catch (Exception e) {@b@ throw new IDGenerationException("Failed to get connection fro oracle-sequence from db factory.", e);@b@ }@b@ }@b@@b@ private boolean setupSequence()@b@ {@b@ int j;@b@ this.sequenceExist = false;@b@ PreparedStatement stmt = null;@b@ ResultSet rs = null;@b@ Connection connection = null;@b@ try@b@ {@b@ connection = getConnection();@b@ stmt = connection.prepareStatement("SELECT INCREMENT_BY, MAX_VALUE FROM seq WHERE SEQUENCE_NAME = ?");@b@ stmt.setString(1, this.seqName);@b@ rs = stmt.executeQuery();@b@@b@ if (rs.next())@b@ {@b@ this.blockSize = rs.getInt(1);@b@ this.maxID = rs.getBigDecimal(2).longValue();@b@ selectNextIdSql = "SELECT " + this.seqName + ".nextval FROM DUAL";@b@ this.sequenceExist = true;@b@ i = 1;@b@@b@ return i;@b@ }@b@ int i = 0;@b@@b@ return i;@b@ }@b@ catch (IDGenerationException e)@b@ {@b@ j = 0;@b@@b@ return j;@b@ }@b@ catch (SQLException e)@b@ {@b@ j = 0;@b@@b@ return j;@b@ }@b@ finally@b@ {@b@ IDGeneratorHelper.closeResultSet(rs);@b@ IDGeneratorHelper.closeStatement(stmt);@b@ IDGeneratorHelper.close(connection);@b@ }@b@ }@b@}