Java之流水号生成器
Posted 阿_毅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之流水号生成器相关的知识,希望对你有一定的参考价值。
开心一笑
视频教程
CSDN学院:
http://edu.csdn.net/lecturer/994
腾讯学院:
网易学院:
http://study.163.com/instructor/1035331499.htm
提出问题
如何使用jAVA生成流水号,同时支持可配置和高并发???
解决问题
假设你们项目已经整合缓存技术
假如你有一定的Java基础
假如……
下面的代码实现的是一个支持高并发,可配置,效率高的流水号生成器,可同时为一个项目的多个模块使用,流水号支持缓存,即每次会预先生成一定数量的流水号存放在缓存中,需要的时候,优先到缓存中去,缓存中的序列号使用完之后,重新生成一定数量的流水号放到缓存中,如此循环,提高效率……
同时,该流水号生成器是线程安全的,使用线程锁进行保护,已经真正的投入到项目中使用……
数据库表设计
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:当前模块 使用的序列号模板
max_serial:存放当前序列号的值
pre_max_num:预生成序列号存放到缓存的个数
is_auto_increment:是否自动增长模式,0:否 1:是
注意:目前序列号模板只支持字母,动态数字(0000 代表1-9999),和日期用${DATE}的组合形式
is_auto_increment配置为1 ,这时配置模板为CX000000生成的序列号为:CX1 ,CX2,CX3…..
配置为0,这时配置模板为CX0000000生成的序列号为:CX00000001,CX00000002,CX00000003
数据库配置说明:如需要项目模块的项目编号,则需要在数据库表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实体设计:
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;
/**
* 是否自动增长标示
*/
@Column(name = "is_auto_increment", columnDefinition = "VARCHAR")
private String isAutoIncrement;
public String getIsAutoIncrement() {
return isAutoIncrement;
}
public void setIsAutoIncrement(String isAutoIncrement) {
this.isAutoIncrement = isAutoIncrement;
}
/**
* 预生成流水号数量
*/
@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接口设计:
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);
/**
* 设置最小值
* @param value 最小值,要求:大于等于零
* @return 流水号生成器实例
*/
ISerialNumService setMin(int value);
/**
* 设置最大值
* @param value 最大值,要求:小于等于Long.MAX_VALUE ( 9223372036854775807 )
* @return 流水号生成器实例
*/
ISerialNumService setMax(long value);
/**
* 设置预生成流水号数量
* @param count 预生成数量
* @return 流水号生成器实例
*/
ISerialNumService setPrepare(int count);
}
Service实现:
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;
/** 预生成锁 */
private final ReentrantLock prepareLock = new ReentrantLock();
/** 最小值 */
private int min = 0;
/** 最大值 */
private long max = 0;
/** 已生成流水号(种子) */
private long seed = min;
/** 预生成数量 */
private int prepare = 0;
/** 数据库存储的当前最大序列号 **/
long maxSerialInt = 0;
/** 当前序列号是否为个位数自增的模式 **/
private String isAutoIncrement = "0";
SystemSerialNumberDTO systemSerialNumberDTO = new SystemSerialNumberDTO();
/** 预生成流水号 */
HashMap<String,List<String>> prepareSerialNumberMap = new HashMap<>();
/**
* 查询单条序列号配置信息
* @param systemSerialNumberDTO
* @return
*/
@Override
public SystemSerialNumberDTO find(SystemSerialNumberDTO systemSerialNumberDTO) {
return serialNumberDAO.find(systemSerialNumberDTO);
}
/**
* 根据模块code生成预数量的序列号存放到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{
//如果动态数字长度大于模板中的长度 例:模板CF000 maxSerialInt 1000
seed = maxSerialInt = 0;
//更新数据,重置maxSerialInt为0
systemSerialNumberDTO.setMaxSerial("0");
SystemSerialNumber systemSerialNumber = new SystemSerialNumber();
BeanUtils.copyProperties(systemSerialNumber,systemSerialNumberDTO);
serialNumberRepository.save(systemSerialNumber);
}
//动态数字生成
String formatSerialNum = format.format(seed);
//动态日期的生成
if(pattern.contains(SerialNumConstants.DATE_SYMBOL)){
String currentDate = DateUtils.format(new Date(),"yyyyMMdd");
formatSerialNum = formatSerialNum.replace(SerialNumConstants.DATE_SYMBOL,currentDate);
}
resultList.add(formatSerialNum);
}
//更新数据
systemSerialNumberDTO.setMaxSerial(maxSerialInt + "");
SystemSerialNumber systemSerialNumber = new SystemSerialNumber();
BeanUtils.copyProperties(systemSerialNumber,systemSerialNumberDTO);
serialNumberRepository.save(systemSerialNumber);
}finally{
lock.unlock();
}
return resultList;
}
/**
* 根据模块code生成序列号
* @param moduleCode 模块code
* @return 序列号
*/
public String generateSerialNumberByModelCode(String moduleCode){
//预序列号加锁
prepareLock.lock();
try{
//判断内存中是否还有序列号
if(null != prepareSerialNumberMap.get(moduleCode) && prepareSerialNumberMap.get(moduleCode).size() > 0){
//若有,返回第一个,并删除
return prepareSerialNumberMap.get(moduleCode).remove(0);
}
}finally {
//预序列号解锁
prepareLock.unlock();
}
systemSerialNumberDTO = new SystemSerialNumberDTO();
systemSerialNumberDTO.setModuleCode(moduleCode);
systemSerialNumberDTO = serialNumberDAO.find(systemSerialNumberDTO);
prepare = Integer.parseInt(systemSerialNumberDTO.getPreMaxNum().trim());//预生成流水号数量
pattern = systemSerialNumberDTO.getConfigTemplet().trim();//配置模板
String maxSerial = systemSerialNumberDTO.getMaxSerial().trim(); //存储当前最大值
isAutoIncrement = systemSerialNumberDTO.getIsAutoIncrement().trim();
maxSerialInt = Long.parseLong(maxSerial.trim());//数据库存储的最大序列号
max = this.counter(pattern,'0') + 1;//根据模板判断当前序列号数字的最大值
if(isAutoIncrement.equals("1")){
pattern = pattern.replace("0","#");
}
format = new DecimalFormat(pattern);
//生成预序列号,存到缓存中
List<String> resultList = generatePrepareSerialNumbers(moduleCode);
prepareLock.lock();
try {
prepareSerialNumberMap.put(moduleCode, resultList);
return prepareSerialNumberMap.get(moduleCode).remove(0);
} finally {
prepareLock.unlock();
}
}
/**
* 设置最小值
*
* @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;
}
/**
* 设置预生成流水号数量
* @param count 预生成数量
* @return 流水号生成器实例
*/
public ISerialNumService setPrepare(int count) {
lock.lock();
try {
this.prepare = count;
}finally {
lock.unlock();
}
return this;
}
/**
* 统计某一个字符出现的次数
* @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;
}
}
读书感悟
- 生活坏到一定程度就会好起来,因为它无法更坏。努力过后,才知道许多事情,坚持坚持,就过来了。
- 有些烦恼,丢掉了,才有云淡风轻的机会。
- 当一个胖纸没有什么不好,最起码可以温暖其他的人。
其他
如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎转载,点赞,顶,欢迎留下宝贵的意见,多谢支持!
以上是关于Java之流水号生成器的主要内容,如果未能解决你的问题,请参考以下文章