- 숫자 형식
- 클러스터링 환경을 지원
키 생성은 데이터베이스 기반으로 구현하였으며 클러스터링 및 성능을 고려하여 키 생성을 담당하는 객체는 미리 정한 크기만큼 미리 수를 가져와 사용하는 방식으로 구현하였다.
이러한 방식이라면 키 생성을 위하여 매번 데이터베이스에 접속하지 않아도 되며, 다수의 키 생성기가 동작하더라도 중복키가 생성되지 않는다.
클래스구현은 springframework 를 사용하여 구현하였다.
package architecture.common.jdbc.sequencer;
public interface Sequencer
{
public abstract String getName();
public abstract long getNext() ;
public abstract int getBlockSize();
public abstract void setBlockSize(int blockSize);
}
package architecture.ee.jdbc.sequencer;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import architecture.common.jdbc.sequencer.Sequencer;
public class SequencerFactory {
private Log log = LogFactory.getLog(getClass());
private DataSource dataSource;
public SequencerFactory(DataSource dataSource) {
this.dataSource = dataSource;
}
public boolean isSetDataSource() {
if (dataSource == null)
return false;
else
return true;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public JdbcSequencer createJdbcSequencer(int sequenceID, String sequencerName, int blockSize) {
JdbcSequencer impl = new JdbcSequencer(sequenceID);
impl.setConfiguration(configuration);
impl.setDataSource(dataSource);
impl.setName(sequencerName);
impl.afterPropertiesSet();
return impl;
}
public Map getAllSequencer() {
Map sequencers = new HashMap();
JdbcTemplate template = new JdbcTemplate(dataSource);
List list = template.query(
"SELECT SEQUENCER_ID, NAME, VALUE FROM V2_SEQUENCER",
new ResultSetExtractor>() {
public List extractData(ResultSet rs) throws SQLException, DataAccessException {
List l = new ArrayList();
while (rs.next()) {
int sequencerID = rs.getInt(1);
String name = rs.getString(2);
long value = rs.getLong(3);
JdbcSequencer sequencer = new JdbcSequencer(sequencerID);
sequencer.setName(name);
l.add(sequencer);
}
return l;
}
});
for (JdbcSequencer sequencer : list) {
sequencer.setConfiguration(configuration);
sequencer.setDataSource(dataSource);
sequencers.put(sequencer.getSequencerId(), sequencer);
}
return sequencers;
}
}
package architecture.ee.jdbc.sequencer;
import java.sql.Types;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import architecture.common.jdbc.sequencer.Sequencer;
public class JdbcSequencer extends JdbcDaoSupport implements Sequencer {
private Log log = LogFactory.getLog(getClass());
private int type;
private String name;
private long currentID;
private long maxID;
private int blockSize;
private Configuration configuration;
public JdbcSequencer() {
super();
}
public JdbcSequencer(int seqType) {
super();
this.type = seqType;
this.blockSize = 10;
this.currentID = 0L;
this.maxID = 0L;
}
public int getSequencerId(){
return type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* @return
* @uml.property name="blockSize"
*/
public int getBlockSize() {
return blockSize;
}
public void setBlockSize(int blockSize) {
this.blockSize = blockSize;
}
public synchronized long getNext() {
if (!(currentID < maxID)) {
getNextBlock(3);
}
long id = currentID;
currentID++;
return id;
}
private long createNewID(int type) {
long newID = 1L ;
getJdbcTemplate().update(getBoundSql("INSERT INTO V2_SEQUENCER (VALUE,NAME,SEQUENCER_ID) VALUES (?, ?, ?)").getSql(), new Object[]{newID, name, type}, new int[]{Types.INTEGER, Types.VARCHAR, Types.INTEGER });
return newID;
}
private int getNextSequencerId(){
int max = getJdbcTemplate().queryForInt("SELECT MAX ( SEQUENCER_ID ) + 1 FROM V2_SEQUENCER");
return max + 1;
}
/**
*
*
* @param count 실패하였을 경우 재 시도 횟수
*/
private void getNextBlock(int count) {
boolean success = false;
long currentID = 1;
try {
currentID = getJdbcTemplate().queryForLong( "SELECT VALUE FROM V2_SEQUENCER WHERE SEQUENCER_ID = ?", new Object[] { this.type }, new int[] { Types.INTEGER });
} catch (IncorrectResultSizeDataAccessException e) {
this.type = getNextSequencerId();
currentID = createNewID(this.type);
}
long newID = currentID + blockSize;
success = (1 == getJdbcTemplate().update("UPDATE V2_SEQUENCER SET VALUE = ? WHERE SEQUENCER_ID = ? AND VALUE = ?",
new Object[] { newID, type, currentID },
new int[] { Types.INTEGER, Types.INTEGER, Types.INTEGER }));
if (success) {
this.currentID = currentID;
this.maxID = newID;
}
if( !success ){
try {
Thread.sleep(75L);
} catch (InterruptedException e) {}
getNextBlock( count - 1);
}
}
@Override
public boolean equals(Object obj) {
JdbcSequencer other = (JdbcSequencer)obj;
return other.getSequencerId() == getSequencerId();
}
}
싱글톤 형태로 생성하여 사용하면 되며 테이블은 아래와 같이 생성한다.
CREATE TABLE V2_SEQUENCER (
SEQUENCER_ID INTEGER NOT NULL,
NAME VARCHAR2(200) NOT NULL,
VALUE INTEGER NOT NULL,
CONSTRAINT V2_SEQUENCER_PK PRIMARY KEY (SEQUENCER_ID)
);
CREATE UNIQUE INDEX V2_SEQUENCER_NAME_IDX ON V2_SEQUENCER (NAME);
COMMENT ON TABLE "V2_SEQUENCER" IS '애플리케이션 전역에서 사용되는 시퀀서 정보';
COMMENT ON COLUMN "V2_SEQUENCER"."SEQUENCER_ID" IS '시퀀서 ID';
COMMENT ON COLUMN "V2_SEQUENCER"."NAME" IS '시퀀서 이름';
COMMENT ON COLUMN "V2_SEQUENCER"."VALUE" IS '시퀀서 값';

댓글 없음:
댓글 쓰기