Java之流水号生成器

article/2025/11/8 16:24:11

开心一笑

这里写图片描述

视频教程

CSDN学院:

http://edu.csdn.net/lecturer/994

腾讯学院:

https://huangwy.ke.qq.com/

网易学院:

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);@Autowiredprivate SerialNumberDAO serialNumberDAO;@Autowiredprivate 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*/@Overridepublic 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 1000seed = maxSerialInt = 0;//更新数据,重置maxSerialInt为0systemSerialNumberDTO.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;}}

读书感悟

  • 生活坏到一定程度就会好起来,因为它无法更坏。努力过后,才知道许多事情,坚持坚持,就过来了。
  • 有些烦恼,丢掉了,才有云淡风轻的机会。
  • 当一个胖纸没有什么不好,最起码可以温暖其他的人。

其他

如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎转载,点赞,顶,欢迎留下宝贵的意见,多谢支持!


http://chatgpt.dhexx.cn/article/YCAMRrSf.shtml

相关文章

业务流水号规则生成组件

对于很多业务系统都需要生成业务流水号&#xff0c;如果订单号、购采单号等等&#xff1b;而这些业务流水号并不是简单的一个增长数值&#xff0c;它们很多时候都有一些不同的规则来定义&#xff0c;如不同类型的字母或地区拼音简写等。为了更灵活生成这些有规则的业务流水号Be…

怎样生成全局唯一流水号?UUID、自增主键,你已经Out啦,快来学习定制化雪花算法。

前言 流水号是每个系统永远都绕不开的一个话题&#xff0c;如订单系统中的订单号&#xff0c;物流系统的运单号、银行系统的业务单号等等&#xff0c;不难发现这些单号虽然叫法不一样&#xff0c;但都有着一些相同的共性&#xff0c;那就是全局唯一性。除此之外&#xff0c;一…

Java自动生成订单编号+流水号

介绍 这里是小编成长之路的历程&#xff0c;也是小编的学习之路。希望和各位大佬们一起成长&#xff01; 以下为小编最喜欢的两句话&#xff1a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 一个人为什么要努力&a…

JAVA如何利用Lock实现多线程并发生成唯一的流水号

本文记录在开发过程&#xff0c;JAVA如何利用ReentrantLock实现多线程并发生成唯一的流水号。 在实际的开发中&#xff0c;我们会经常碰到需要生成唯一流水号的业务场景。有些情况可以采用数据库自定义序列号自增生成流水号&#xff0c;亦或是自己编写数据库触发器生成流水号。…

mysql 生成流水号 存储过程 订单编号

用存储过程生成流水号是很常用的&#xff0c;这里以生成订单编号的流水号作为示例。&#xff08;新的一天的流水号从1开始&#xff0c;如&#xff1a;今天的订单编号是CD2013010900014&#xff0c;下一个订单编号将是CD2013010900015&#xff1b;明天的订单编号将从CD201301100…

流水号生成规则

流水号生成规则 从“0001”号起始&#xff0c;依序不跳跃不间断地编号&#xff0c;形成流水编码&#xff0c;依次为0001、0002、0003、0004、0005、0006…等。当编至“9999”号&#xff0c;仍需继续编号时&#xff0c;从“A000”号&#xff08;A000代表10000&#xff09;起始重…

一种生成流水号的方法

1.介绍 今天做了一个功能&#xff0c;生成订单流水号&#xff0c;当然这其实这并不是一个很难的功能&#xff0c;最直接的方式就是日期主机Id随机字符串来拼接一个流水号。但是今天有个我认为比较优雅方式来实现。我要介绍是日期 long&#xff08;商家Id订单类型主机IDAtomicIn…

简单介绍订单号或者流水号的生成方法

一般订单号或者流水号等可能在一些平台会用到&#xff0c;然后我就简单的介绍一个我自己生成订单号和流水号的一个方法吧&#xff0c;如果程序有问题或者你有更好的生成办法&#xff0c;欢迎留言&#xff0c;留下你的文章链接&#xff0c;我们一起学习和进步哈。 方法简介&…

如何使用redis生成流水号

概述 本文讲述如何使用redis生成流水号。本文是在Springboot中实现的。知道原理之后其他框架也可以轻松实现。 原理介绍 本文主要是使用redis的incr方法进行自增补零。然后结合时间、随机数、前缀组成唯一的流水号。 下面是流水号的结构。 在文章的最后还是简单介绍一下redis的…

谈谈订单号和流水号的关系

订单号和流水号是不同的。 首先订单号是订单唯一的编号&#xff0c;而且电商平台的各种子系统也是根据订单来统计业务完成的情况&#xff0c;订单编号经常用来被查询&#xff0c;所以数据类型必须是数字&#xff0c;而且是全局唯一&#xff0c;那肯定就得主键字段了。 然后流水…

低代码学习教程:生成固定格式流水号

方法1&#xff1a;RECNO()方法2&#xff1a;MAPX() 表单设计中经常涉及流水号的制作问题&#xff0c;下面就分别介绍下两种编号的实现方法&#xff0c;大家可以根据需要自行选择。 注意&#xff1a; 百数已支持【流水号】控件&#xff0c;如有特殊要求可参考文档&#xff1a;…

【26天高效学习Java编程】Day19:Java 多线程

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;由浅入深讲解Java的基本使用&#xff0c;希望大家都能够从中有所收获&#xff0c;也请大家多多支持。 专栏地址:26天高效学习Java编程 相关软件地址:软件地址 所有代码地址:代码地址 如果文章知识点有错误的地方&#x…

怎么入门学习Java编程

因为目前java非常火,应用非常的广泛,是目前最火的行业之一,竞争很大,工资很高,未来发展也极好。 如条件还可以,负担不是那么大,可以选择培训,培训一定会比你自学的好,如果培训都学不好,自学肯定更难。目前java的培训费用都是2W+,这还只是培训费而已,加上一些其他的…

Java学习

集合 什么是集合&#xff1f; 集合&#xff1a;集合是java中提供的一种容器&#xff0c;可以用来存储多个数据。 集合和数组的区别 数组的长度是固定的。集合的长度是可变的。 数组中存储的是同一类型的元素&#xff0c;可以存储任意类型数据。集合存储的都是引用数据类型。如…

想学习Java编程,看书还是看视频更合适?

首先&#xff1a;自己本身就是初级或者零基础的&#xff0c;自己对软件了解的都不足够&#xff0c;跟着视频学&#xff0c;老师操作操作一步你就能看着他操作&#xff0c;这样心里更有谱。 第二&#xff1a;跟着视频学能学的更好&#xff0c;知识体系更全&#xff0c;一般视频…

自学过来人告诉你,初学者应该怎么快速的学习Java编程?

我说说我个人的案例吧&#xff0c;我电子信息专业&#xff0c;后来选择做了Java开发&#xff0c;在11年的时候开始学习的Java&#xff0c;可以说那时候的企业要求低于现在&#xff0c;我当时学习由于没有钱&#xff0c;我是自学的&#xff0c;我大学学过C语言 我晚上下班的时候…

通过项目驱动的学习方法快速掌握Java编程

摘要 Java作为一种广泛应用于软件开发领域的编程语言&#xff0c;对于零基础的学习者来说&#xff0c;学习Java编程可能存在一定的难度。本文将介绍如何通过项目驱动的学习方法&#xff0c;帮助零起点的学习者快速掌握Java编程。通过以项目为核心的学习路径、结合实践和理论的…

Java学习之编程入门

0 编程入门 0.1 概述0.2 计算机硬件介绍0.2.1 中央处理器0.2.2 存储设备0.2.3 内存0.2.4 输入和输出设备0.2.5 通信设备 0.3 计算机发展史上的鼻祖0.4 操作系统0.5 万维网0.6 职业发展与提升0.7 学习经验探讨 0.1 概述 计算机包括硬件(hardware)和软件(software)两部分。 硬件…

学习Java编程知识 必知要点

Java 是全球最受欢迎的编程语言之一&#xff0c;在世界编程语言排行榜 TIOBE 中&#xff0c;Java 一直霸占着前三名&#xff0c;有好多年甚至都是第一名。那么如此强大的Java你真的了解他的知识体系吗&#xff1f;他的学习路线你知道吗&#xff1f; 1. Java虚拟机——JVM JVM&a…

java三大平台介绍,选择哪个平台学习java编程?

&#x1f482; 个人主页: IT学习日记&#x1f91f; 版权: 本文由【IT学习日记】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦&#x1f485; 想寻找共同成长的小伙伴&#xff0c;请点击【技术圈子】 眼见…