java 流水_Java之流水号生成器实现

article/2025/11/8 1:36:47

开心一笑

331b872e9c8f

搞笑.png

提出问题

如何使用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> 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 generatePrepareSerialNumbers(String moduleCode){

//临时List变量

List resultList = new ArrayList(prepare);

lock.lock();

try{

for(int i=0;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 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/aHfeeBe1.shtml

相关文章

哈希一刀流——布隆过滤器详解

目录 传统艺能&#x1f60e;背景&#x1f914;概念&#x1f914;误判控制&#x1f60b;具体实现&#x1f60b;插入&#x1f60b;查找&#x1f60b;删除&#x1f60b;优劣&#x1f60b;使用场景&#x1f60b; 传统艺能&#x1f60e; 小编是双非本科大二菜鸟不赘述&#xff0c;欢…

连米哈游都成了第二,这个一刀999的页游大王把老外吃透了。

自从《 原神 》在 2020 年上线之后&#xff0c;米哈游基本就稳坐出海收入第一的王座。 但是&#xff0c;稳坐榜首的米哈游&#xff0c;却在今年 10 月&#xff0c;被突然冒出的三七互娱一脚踢了下来。 没错&#xff0c;就是那个做出了《 大天使之剑 》、《 传奇霸业 》的页游大…

如何产生一个全局唯一的流水号(附demo)

本文介绍如何使用最简单的方法产生一个全局唯一的流水号&#xff0c;支持集群&#xff0c;性能可靠&#xff0c;并且经过实际的应用 唯一流水号的格式为当前系统时间当前服务器编号并发序列号&#xff0c;长度最短可为17位&#xff0c;每毫秒支持生成多个并且支持集群部署 废话…

记小辉人生中的第一刀

本篇是小辉趴在床上写完的博客&#xff0c;讲的是半个月前“肛裂肛瘘混合痔”手术到术后两周的康复过程以及一些注意事项和提示&#xff0c;给读者们的健康提个醒&#xff0c;欢迎转发给身边正在经历痛苦或者犹豫的朋友。 希望大家都身体健康&#xff0c;永远用不上我下面提到的…

砍一刀免费拿营销玩法解说

大家应该都收到拼多多的砍价链接&#xff0c;小来对拼多多这种扰民是推广方式非常反感&#xff0c;但不得不承认&#xff0c;这个营销方式却是拼多多最有效的营销方式之一。 拼多多最新的财报显示&#xff0c;拼多多2020年活跃买家数为7.884亿&#xff0c;首度超越阿里位居全国…

【leetCode:剑指 Offer】06. 从尾到头打印链表

1.题目描述 输入一个链表的头节点&#xff0c;从尾到头反过来返回每个节点的值&#xff08;用数组返回&#xff09;。 2.算法分析 倒序输出&#xff0c;使用栈Stack的数据结构。 先将链表中的元素入栈&#xff0c;然后遍历栈内元素&#xff0c;将元素加入到数组中。 3.代码实现…

流水

这几天怎么过去的 在纸上画一笔 就这么轻松 没和任何人联系 似乎很忙 总是在上课上课上课 从这个教室到那个教室 从这个实验室到那个实验室 嗜睡得厉害 心里很空 很久没有S的消息 有点冷

R语言--readr包读写数据

文章目录 前言一、发现问题二、分析问题三、解决问题四、读写速度总结吐槽 前言 当你使出了浑身解数&#xff0c;read.csv和read.table还是无法读入数据时&#xff0c;或许可以尝试一下readr包中的read_table&#xff0c;read_csv等函数。尝试一下&#xff01; 我是一个很专一…

R语言入门:读取csv文件及获取统计数值(1)

本次编程尝试使用R语言读取csv文件“filesize.csv”&#xff0c;并根据内容绘制统计图和得出平均数&#xff0c;中位数和方差等简单数据。 csv文件内容1001个数值至少大于1000的小数组成&#xff0c;每行一个&#xff0c;共计1001行 在打开文件之前&#xff0c;我们可能会遇到…

R语言 读取文件

1. R读取txt文件 使用R读取txt文件直接使用read.table()方法进行读取即可&#xff0c;不需要加载额外的包。 read.table("/home/slave/test.txt",headerT,na.strings c("NA"))注意&#xff0c;此处的na.strings c("NA") 的意思是文件中的缺失数…

R从文件中读取数据,输出文件

看了几天的书&#xff0c;终于到这一步了&#xff0c;说实话&#xff0c;用R来做统计&#xff0c;很少有人手动的去输入那些数字&#xff0c;肯定是从别的地方导入的&#xff0c;我们用来处理就可以了&#xff0c;所以到这里才算是真正的入门&#xff0c;前面都是做基础的练手。…

R语言中文件或数据的读写

文章目录 系统数据的读写xlsx文件的读写举例说明具体代码结果如下 csv文件的读写读取csv文件写入csv文件使用readr包读取csv文件使用data.table包读取csv文件 scan函数scan()函数的使用语法参数含义描述实例&#xff1a; 系统数据的读写 save(a,b,file"test.RData")…

掌握R语言文件读取方法

目标 掌握 R语言文件读取方法 学习笔记 utils包内Date Input用法base包内readLines用法stringi包内stri_read_linesxlsx包内Date Input用法readr包内 Read a delimited file 用法 1.utils包内Date Input用法 以read.table为例。 read.table参数详细说明见http://www.360doc…

R语言入门(15)_读取文件(read)

目录 一、read.table() 读取工作路径下的纯文本文件&#xff08;.txt&#xff09;(.csv) 1、工作路径的设置 2、head函数——只显示数据前几行 3、read.table()的其他一些参数 二、与read.table相类似的函数 三、read.table()读取网络上的文本文件 三、读取非文本文件 …

使用R读取并查看数据

本篇文章介绍如何使用R读取并查看数据&#xff0c;包含一些最基础的函数使用方法和说明。后面还会陆续介绍数据清洗&#xff0c;匹配和提取等相关的操作。 查看函数帮助 对于新手来说&#xff0c;在使用R时最重要的是了解不同函数的使用方法。很多时候我们都是边用边学的状态&…

R语言如何从外部读取数据到R中

R语言可以从键盘&#xff0c;文本&#xff0c;excel&#xff0c;access&#xff0c;数据库&#xff0c;专业处理软件sas 一、使用键盘的输入 mydata<-data.frame(agenumeric(0),gendercharacter(0),weightnumeric(0)) mydata<-edit(mydata) 二、读入带有分隔符文本格式…

R语言入门——数据快速读取与查看(含实例代码和参数讲解)

R语言数据读取 介绍引言结构安排 数据读取函数文本数据readLines函数 键盘键入数据scan函数讲解 表格数据.xlsx文件介绍表格数据函数参数介绍 快速读入参数介绍竞赛数据练习2020美赛C题2019华为杯E题 数据查看函数表 介绍 引言 毕业季来临&#xff0c;很多小伙伴都在积极准备…

R语言读CSV、txt文件方式以及read.table read.csv 和readr(大数据读取包)

首先准备测试数据*(mtcars)分别为CSV. TXT **2018博客之星评选&#xff0c;如果喜欢我的文章&#xff0c;请投我一票&#xff0c;编号&#xff1a;No.009** [支持连接](https://blog.csdn.net/HHTNAN/article/details/85330758) ,万分感谢&#xff01;&#xff01;&#x…

R语言之读取文件夹的数据

读取文件路径&#xff1a;一层目录&#xff08;“示例”&#xff09;、二层目录&#xff08;“数据1”、“数据2”&#xff09;下的表格数据。 “示例”文件下&#xff1a; “数据1”文件下&#xff1a; “数据2”文件下&#xff1a; 读取文件夹 rm(listls()) #清除变量 …

R入门(一)----读取数据、查看数据

感谢大鹏dapengde 创建数据 #在相应位置新建文件夹 dir.create(E:/R/R lab/学R/r4r) #将数据文件存进文件夹 write.csv(as.data.frame(t(matrix(co2,12,dimnames list(month.abb,unique(floor(time(co2))) ) ))),file E:/R/R lab/学R/r4r/co2.csv)读取数据 R数据的导入与…