首页

关于topcoder通过IDGenerator序列生成器实现生成整型常用id唯一标识符

标签:id生成器,序列,sequences,topcoder,id_generator     发布时间:2018-06-12   

一、前言

关于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@}