首页 > 编程语言 >Java之流水号生成器实现

Java之流水号生成器实现

时间:2023-08-01 18:33:44浏览次数:47  
标签:Java String 生成器 流水号 import return 序列号 public

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

}

读书感悟

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

标签:Java,String,生成器,流水号,import,return,序列号,public
From: https://www.cnblogs.com/sunny3158/p/17598742.html

相关文章

  • java打印日志时,如何对字段进行脱敏?
    java打印日志时,如何对字段进行脱敏?原文链接:https://blog.csdn.net/weixin_43901749/article/details/129150818第一步,创建类继承MessageConverter,重写convert方法,添加注解@Component("sensitive")第二步,在logback.xml中增加conversionRule标签在我们开发项目的时候,有些......
  • java日志脱敏(密码/身份证/其他自定义等)logback
    java日志脱敏(密码/身份证/其他自定义等)logback原文链接:https://blog.csdn.net/weixin_39286166/article/details/126889660一.脱敏规则类 importch.qos.logback.classic.pattern.MessageConverter;importch.qos.logback.classic.spi.ILoggingEvent;importorg.apache.c......
  • java中使用异步方式调用接口@Async
    @Async使用:1、首先在启动类上开启注解@EnableAsync2、然后需要异步操作的方法上加上@Async*/publicclassAsyncTest{@Asyncpublicvoidtest()throwsInterruptedException{//做处理Thread.sleep(1000);}/**如果需要返回值的话,通过AsyncResult进行封装*/@AsyncpublicF......
  • javascript按钮通过cookie限制60s后才可以点击
    javascript按钮通过cookie限制60s后才可以点击1️⃣首先创建一个html页面,放入一个按钮 2️⃣设置点击按钮的触发函数一般当点击按钮都会有一些业务需要,在需求结束后,触发saveCookie的方法 3️⃣saveCookie方法当点击查询按钮之后,触发saveCooike方法,按钮倒计时需要一个结束......
  • JavaIO流
    JavaIO流基础概念数据流:一组有序,有起点和终点的字节的数据序列。包括输入流和输出流输入流:程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道输出流:程序向输出流写入数据。将程序中的数据输出到外界(显示器、打......
  • 使用Java进行串口通信
    引言 由于java的平台无关特性使得串口编程很困难。因为串口需要一个与特定平台实现的标准的API,而这对于java来说很困难。不幸的是,Sun在java的串口通信上没有太多关注。Sun已经定义了一个叫做JavaComm的串口通信API,但它的实现却不是javaSE(标准版)的一部分。Sun只为少数java平台提......
  • 每个Java开发者都应该知道的5个JDK工具
    JDK是Java语言的软件开发工具包,没有它就无法编译Java程序。目前,有许许多多的JDK工具呈现在大家面前,但最常用的莫过于java.exe、javac.exe、jar等。除了这几个,还有哪些呢?本文作者Joe拥有多年的Java开发经验,其在博客上分享了一篇文章:5JDKToolsEveryJavaDeveloperShouldKnow,笔......
  • 计算机基础与JavaScript初识
    一:编程介绍编程:就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程.计算机程序:就是计算机所执行的一系列的指令集合,而程序全部都是用我们所掌握的语言来编写的,所以人们要控制计算机一定要通过计算机语言向计算机发出命令。从事编程的人员,就是程......
  • java jsch sftp 中文乱码解决方案
    不同jsch版本对比--->JavaSSH/Sftp库——JSch/SSHJ方案一:maven版本:<dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version></dependency>代码案例:packagecom.ch......
  • 从Java后端获取时间配置字符串,并在前端使用它来设置默认公布时间。
    <divclass="layui-inline"id="AItem"><labelclass="layui-form-labelsyn-form-item-require">公布时间:</label><divclass="layui-input-block">&......