sunny123456

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  1796 随笔 :: 22 文章 :: 24 评论 :: 226万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

Java之流水号生成器实现
原文链接:https://www.jianshu.com/p/331b872e9c8f

开心一笑

提出问题

如何使用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);



     <span class="token comment">//动态日期的生成</span>
     <span class="token keyword">if</span><span class="token punctuation">(</span>pattern<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token class-name">SerialNumConstants</span><span class="token punctuation">.</span>DATE_SYMBOL<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
         <span class="token class-name">String</span> currentDate <span class="token operator">=</span> <span class="token class-name">DateUtils</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token string">"yyyyMMdd"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
         formatSerialNum <span class="token operator">=</span> formatSerialNum<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token class-name">SerialNumConstants</span><span class="token punctuation">.</span>DATE_SYMBOL<span class="token punctuation">,</span>currentDate<span class="token punctuation">)</span><span class="token punctuation">;</span>
     <span class="token punctuation">}</span>

     resultList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>formatSerialNum<span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span>
 <span class="token comment">//更新数据</span>
 systemSerialNumberDTO<span class="token punctuation">.</span><span class="token function">setMaxSerial</span><span class="token punctuation">(</span>maxSerialInt <span class="token operator">+</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token class-name">SystemSerialNumber</span> systemSerialNumber <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SystemSerialNumber</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>systemSerialNumber<span class="token punctuation">,</span>systemSerialNumberDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>
 serialNumberRepository<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>systemSerialNumber<span class="token punctuation">)</span><span class="token punctuation">;</span>




}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;

}







     <span class="token comment">//动态日期的生成</span>
     <span class="token keyword">if</span><span class="token punctuation">(</span>pattern<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token class-name">SerialNumConstants</span><span class="token punctuation">.</span>DATE_SYMBOL<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
         <span class="token class-name">String</span> currentDate <span class="token operator">=</span> <span class="token class-name">DateUtils</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token string">"yyyyMMdd"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
         formatSerialNum <span class="token operator">=</span> formatSerialNum<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token class-name">SerialNumConstants</span><span class="token punctuation">.</span>DATE_SYMBOL<span class="token punctuation">,</span>currentDate<span class="token punctuation">)</span><span class="token punctuation">;</span>
     <span class="token punctuation">}</span>

     resultList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>formatSerialNum<span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span>
 <span class="token comment">//更新数据</span>
 systemSerialNumberDTO<span class="token punctuation">.</span><span class="token function">setMaxSerial</span><span class="token punctuation">(</span>maxSerialInt <span class="token operator">+</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token class-name">SystemSerialNumber</span> systemSerialNumber <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SystemSerialNumber</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>systemSerialNumber<span class="token punctuation">,</span>systemSerialNumberDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>
 serialNumberRepository<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>systemSerialNumber<span class="token punctuation">)</span><span class="token punctuation">;</span>
}

读书感悟

  • 生活坏到一定程度就会好起来,因为它无法更坏。努力过后,才知道许多事情,坚持坚持,就过来了。
  • 有些烦恼,丢掉了,才有云淡风轻的机会。
  • 当一个胖纸没有什么不好,最起码可以温暖其他的人。
posted on   sunny123456  阅读(525)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2021-08-01 $(document).ready和window.onload区别
2021-08-01 为什么script标签一般放在body下面
2021-08-01 idea添加jar和移除jar包的三种方式
2021-08-01 HTML转义字符大全(转)
2021-08-01 C#中 以固定长度 取字符串中的数据
2021-08-01 Oracle的substr函数简单用法和 C#中一样
2021-08-01 ORA-22835:缓冲区对于CLOB到CHAR转换而言太小
点击右上角即可分享
微信分享提示