開心一笑
提出問題
如何使用jAVA生成流水號梭姓,同時支持可配置和高并發(fā)???
解決問題
假設(shè)你們項目已經(jīng)整合緩存技術(shù)
假如你有一定的Java基礎(chǔ)
假如......
下面的代碼實現(xiàn)的是一個支持高并發(fā),可配置嘉熊,效率高的流水號生成器涂召,
可同時為一個項目的多個模塊使用供炼,流水號支持緩存昔头,即每次會預(yù)先生成一定數(shù)量的流水號存放在緩存中洛口,
需要的時候,優(yōu)先到緩存中去蹂空,緩存中的序列號使用完之后俯萌,重新生成一定數(shù)量的流水號放到緩存中,如此循環(huán)上枕,提高效率......
同時咐熙,該流水號生成器是線程安全的,使用線程鎖進(jìn)行保護(hù),已經(jīng)真正的投入到項目中使用......
數(shù)據(jù)庫表設(shè)計
CREATE TABLE sys_serial_number2 (
"id" varchar(32) COLLATE "default" NOT NULL,
"module_name" varchar(50) COLLATE "default",
"module_code" varchar(50) COLLATE "default",
"config_templet" varchar(50) COLLATE "default",
"max_serial" varchar(32) COLLATE "default",
"pre_max_num" varchar(32) COLLATE "default",
"is_auto_increment" char(1) COLLATE "default"
)
說明:
module_name:模塊名稱
module_code:模塊編碼
config_templet:當(dāng)前模塊 使用的序列號模板
max_serial:存放當(dāng)前序列號的值
pre_max_num:預(yù)生成序列號存放到緩存的個數(shù)
is_auto_increment:是否自動增長模式辨萍,0:否 1:是
注意:目前序列號模板只支持字母棋恼,動態(tài)數(shù)字(0000 代表1-9999),和日期用${DATE}的組合形式
is_auto_increment配置為1 锈玉,這時配置模板為CX000000生成的序列號為:CX1 ,CX2爪飘,CX3.....
配置為0,這時配置模板為CX0000000生成的序列號為:CX00000001,CX00000002拉背,CX00000003
數(shù)據(jù)庫配置說明:如需要項目模塊的項目編號师崎,則需要在數(shù)據(jù)庫表sys_serial_number中配置一條記錄:
| id | module_name | module_code | config_templet | max_serial | pre_max_num | is_auto_increment
|-------|--------------|--------------|-----------------|-------------|-------------|--------------------/
| xxxx | 項目 | PJ |CX00000000${DATE}| 2650 | 100 | 1
CX00000000${DATE}生成的序列號類似于:CX0000000120160522 ,CX0000000220160522椅棺,CX0000000320160522 ......
序列號model實體設(shè)計:
package com.evada.de.serialnum.model;
import com.evada.de.common.model.BaseModel;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* 功能描述:序列號表模型
*
* @author :Ay 2015/11/23
*/
@Entity
@Table(name="sys_serial_number")
public class SystemSerialNumber extends BaseModel {
/**
* 模塊名稱
*/
@Column(name = "module_name", columnDefinition = "VARCHAR")
private String moduleName;
/**
* 模塊編碼
*/
@Column(name = "module_code", columnDefinition = "VARCHAR")
private String moduleCode;
/**
* 流水號配置模板
*/
@Column(name = "config_templet", columnDefinition = "VARCHAR")
private String configTemplet;
/**
* 序列號最大值
*/
@Column(name = "max_serial", columnDefinition = "VARCHAR")
private String maxSerial;
/**
* 是否自動增長標(biāo)示
*/
@Column(name = "is_auto_increment", columnDefinition = "VARCHAR")
private String isAutoIncrement;
public String getIsAutoIncrement() {
return isAutoIncrement;
}
public void setIsAutoIncrement(String isAutoIncrement) {
this.isAutoIncrement = isAutoIncrement;
}
/**
* 預(yù)生成流水號數(shù)量
*/
@Column(name = "pre_max_num", columnDefinition = "VARCHAR")
private String preMaxNum;
public String getPreMaxNum() {
return preMaxNum;
}
public void setPreMaxNum(String preMaxNum) {
this.preMaxNum = preMaxNum;
}
public String getModuleName() {
return moduleName;
}
public void setModuleName(String moduleName) {
this.moduleName = moduleName;
}
public String getModuleCode() {
return moduleCode;
}
public void setModuleCode(String moduleCode) {
this.moduleCode = moduleCode;
}
public String getConfigTemplet() {
return configTemplet;
}
public void setConfigTemplet(String configTemplet) {
this.configTemplet = configTemplet;
}
public String getMaxSerial() {
return maxSerial;
}
public void setMaxSerial(String maxSerial) {
this.maxSerial = maxSerial;
}
public SystemSerialNumber(String id){
this.id = id;
}
public SystemSerialNumber(String id,String moduleCode){
this.id = id;
this.moduleCode = moduleCode;
}
public SystemSerialNumber(){}
}
Service接口設(shè)計:
package com.evada.de.serialnum.service;
import com.evada.de.serialnum.dto.SystemSerialNumberDTO;
/**
* 序列號service接口
* Created by huangwy on 2015/11/24.
*/
public interface ISerialNumService {
public SystemSerialNumberDTO find(SystemSerialNumberDTO systemSerialNumberDTO);
public String generateSerialNumberByModelCode(String moduleCode);
/**
* 設(shè)置最小值
* @param value 最小值犁罩,要求:大于等于零
* @return 流水號生成器實例
*/
ISerialNumService setMin(int value);
/**
* 設(shè)置最大值
* @param value 最大值齐蔽,要求:小于等于Long.MAX_VALUE ( 9223372036854775807 )
* @return 流水號生成器實例
*/
ISerialNumService setMax(long value);
/**
* 設(shè)置預(yù)生成流水號數(shù)量
* @param count 預(yù)生成數(shù)量
* @return 流水號生成器實例
*/
ISerialNumService setPrepare(int count);
}
Service實現(xiàn):
package com.evada.de.serialnum.service.impl;
import com.evada.de.common.constants.SerialNumConstants;
import com.evada.de.serialnum.dto.SystemSerialNumberDTO;
import com.evada.de.serialnum.model.SystemSerialNumber;
import com.evada.de.serialnum.repository.SerialNumberRepository;
import com.evada.de.serialnum.repository.mybatis.SerialNumberDAO;
import com.evada.de.serialnum.service.ISerialNumService;
import com.evada.inno.common.util.BeanUtils;
import com.evada.inno.common.util.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by Ay on 2015/11/24.
*/
@Service("serialNumberService")
public class SerialNumberServiceImpl implements ISerialNumService {
private static final Logger LOGGER = LoggerFactory.getLogger(SerialNumberServiceImpl.class);
@Autowired
private SerialNumberDAO serialNumberDAO;
@Autowired
private SerialNumberRepository serialNumberRepository;
/** 格式 */
private String pattern = "";
/** 生成器鎖 */
private final ReentrantLock lock = new ReentrantLock();
/** 流水號格式化器 */
private DecimalFormat format = null;
/** 預(yù)生成鎖 */
private final ReentrantLock prepareLock = new ReentrantLock();
/** 最小值 */
private int min = 0;
/** 最大值 */
private long max = 0;
/** 已生成流水號(種子) */
private long seed = min;
/** 預(yù)生成數(shù)量 */
private int prepare = 0;
/** 數(shù)據(jù)庫存儲的當(dāng)前最大序列號 **/
long maxSerialInt = 0;
/** 當(dāng)前序列號是否為個位數(shù)自增的模式 **/
private String isAutoIncrement = "0";
SystemSerialNumberDTO systemSerialNumberDTO = new SystemSerialNumberDTO();
/** 預(yù)生成流水號 */
HashMap<String,List<String>> prepareSerialNumberMap = new HashMap<>();
/**
* 查詢單條序列號配置信息
* @param systemSerialNumberDTO
* @return
*/
@Override
public SystemSerialNumberDTO find(SystemSerialNumberDTO systemSerialNumberDTO) {
return serialNumberDAO.find(systemSerialNumberDTO);
}
/**
* 根據(jù)模塊code生成預(yù)數(shù)量的序列號存放到Map中
* @param moduleCode 模塊code
* @return
*/
@CachePut(value = "serialNumber",key="#moduleCode")
public List<String> generatePrepareSerialNumbers(String moduleCode){
//臨時List變量
List<String> resultList = new ArrayList<String>(prepare);
lock.lock();
try{
for(int i=0;i<prepare;i++){
maxSerialInt = maxSerialInt + 1;
if(maxSerialInt > min && (maxSerialInt + "").length() < max ){
seed = maxSerialInt ;
}else{
//如果動態(tài)數(shù)字長度大于模板中的長度 例:模板CF000 maxSerialInt 1000
seed = maxSerialInt = 0;
//更新數(shù)據(jù),重置maxSerialInt為0
systemSerialNumberDTO.setMaxSerial("0");
SystemSerialNumber systemSerialNumber = new SystemSerialNumber();
BeanUtils.copyProperties(systemSerialNumber,systemSerialNumberDTO);
serialNumberRepository.save(systemSerialNumber);
}
//動態(tài)數(shù)字生成
String formatSerialNum = format.format(seed);
//動態(tài)日期的生成
if(pattern.contains(SerialNumConstants.DATE_SYMBOL)){
String currentDate = DateUtils.format(new Date(),"yyyyMMdd");
formatSerialNum = formatSerialNum.replace(SerialNumConstants.DATE_SYMBOL,currentDate);
}
resultList.add(formatSerialNum);
}
//更新數(shù)據(jù)
systemSerialNumberDTO.setMaxSerial(maxSerialInt + "");
SystemSerialNumber systemSerialNumber = new SystemSerialNumber();
BeanUtils.copyProperties(systemSerialNumber,systemSerialNumberDTO);
serialNumberRepository.save(systemSerialNumber);
}finally{
lock.unlock();
}
return resultList;
}
/**
* 根據(jù)模塊code生成序列號
* @param moduleCode 模塊code
* @return 序列號
*/
public String generateSerialNumberByModelCode(String moduleCode){
//預(yù)序列號加鎖
prepareLock.lock();
try{
//判斷內(nèi)存中是否還有序列號
if(null != prepareSerialNumberMap.get(moduleCode) && prepareSerialNumberMap.get(moduleCode).size() > 0){
//若有昼汗,返回第一個肴熏,并刪除
return prepareSerialNumberMap.get(moduleCode).remove(0);
}
}finally {
//預(yù)序列號解鎖
prepareLock.unlock();
}
systemSerialNumberDTO = new SystemSerialNumberDTO();
systemSerialNumberDTO.setModuleCode(moduleCode);
systemSerialNumberDTO = serialNumberDAO.find(systemSerialNumberDTO);
prepare = Integer.parseInt(systemSerialNumberDTO.getPreMaxNum().trim());//預(yù)生成流水號數(shù)量
pattern = systemSerialNumberDTO.getConfigTemplet().trim();//配置模板
String maxSerial = systemSerialNumberDTO.getMaxSerial().trim(); //存儲當(dāng)前最大值
isAutoIncrement = systemSerialNumberDTO.getIsAutoIncrement().trim();
maxSerialInt = Long.parseLong(maxSerial.trim());//數(shù)據(jù)庫存儲的最大序列號
max = this.counter(pattern,'0') + 1;//根據(jù)模板判斷當(dāng)前序列號數(shù)字的最大值
if(isAutoIncrement.equals("1")){
pattern = pattern.replace("0","#");
}
format = new DecimalFormat(pattern);
//生成預(yù)序列號,存到緩存中
List<String> resultList = generatePrepareSerialNumbers(moduleCode);
prepareLock.lock();
try {
prepareSerialNumberMap.put(moduleCode, resultList);
return prepareSerialNumberMap.get(moduleCode).remove(0);
} finally {
prepareLock.unlock();
}
}
/**
* 設(shè)置最小值
*
* @param value 最小值顷窒,要求:大于等于零
* @return 流水號生成器實例
*/
public ISerialNumService setMin(int value) {
lock.lock();
try {
this.min = value;
}finally {
lock.unlock();
}
return this;
}
/**
* 最大值
*
* @param value 最大值蛙吏,要求:小于等于Long.MAX_VALUE ( 9223372036854775807 )
* @return 流水號生成器實例
*/
public ISerialNumService setMax(long value) {
lock.lock();
try {
this.max = value;
}finally {
lock.unlock();
}
return this;
}
/**
* 設(shè)置預(yù)生成流水號數(shù)量
* @param count 預(yù)生成數(shù)量
* @return 流水號生成器實例
*/
public ISerialNumService setPrepare(int count) {
lock.lock();
try {
this.prepare = count;
}finally {
lock.unlock();
}
return this;
}
/**
* 統(tǒng)計某一個字符出現(xiàn)的次數(shù)
* @param str 查找的字符
* @param c
* @return
*/
private int counter(String str,char c){
int count=0;
for(int i = 0;i < str.length();i++){
if(str.charAt(i)==c){
count++;
}
}
return count;
}
}
讀書感悟
- 生活壞到一定程度就會好起來,因為它無法更壞鞋吉。努力過后鸦做,才知道許多事情,堅持堅持谓着,就過來了泼诱。
- 有些煩惱,丟掉了赊锚,才有云淡風(fēng)輕的機(jī)會治筒。
- 當(dāng)一個胖紙沒有什么不好,最起碼可以溫暖其他的人舷蒲。