Java之流水號(hào)生成器實(shí)現(xiàn)

開心一笑

搞笑.png

提出問題

如何使用jAVA生成流水號(hào),同時(shí)支持可配置和高并發(fā)???

解決問題

假設(shè)你們項(xiàng)目已經(jīng)整合緩存技術(shù)
假如你有一定的Java基礎(chǔ)
假如......

下面的代碼實(shí)現(xiàn)的是一個(gè)支持高并發(fā),可配置,效率高的流水號(hào)生成器,
可同時(shí)為一個(gè)項(xiàng)目的多個(gè)模塊使用,流水號(hào)支持緩存,即每次會(huì)預(yù)先生成一定數(shù)量的流水號(hào)存放在緩存中,
需要的時(shí)候,優(yōu)先到緩存中去,緩存中的序列號(hào)使用完之后,重新生成一定數(shù)量的流水號(hào)放到緩存中,如此循環(huán),提高效率......
同時(shí),該流水號(hào)生成器是線程安全的,使用線程鎖進(jìn)行保護(hù),已經(jīng)真正的投入到項(xiàng)目中使用......

數(shù)據(jù)庫表設(shè)計(jì)

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:當(dāng)前模塊 使用的序列號(hào)模板
max_serial:存放當(dāng)前序列號(hào)的值
pre_max_num:預(yù)生成序列號(hào)存放到緩存的個(gè)數(shù)
is_auto_increment:是否自動(dòng)增長(zhǎng)模式,0:否  1:是

注意:目前序列號(hào)模板只支持字母,動(dòng)態(tài)數(shù)字(0000 代表1-9999),和日期用${DATE}的組合形式
is_auto_increment配置為1 ,這時(shí)配置模板為CX000000生成的序列號(hào)為:CX1 ,CX2,CX3.....
配置為0,這時(shí)配置模板為CX0000000生成的序列號(hào)為:CX00000001,CX00000002,CX00000003

數(shù)據(jù)庫配置說明:如需要項(xiàng)目模塊的項(xiàng)目編號(hào),則需要在數(shù)據(jù)庫表sys_serial_number中配置一條記錄:

|  id   |  module_name |  module_code |  config_templet | max_serial  | pre_max_num |  is_auto_increment
|-------|--------------|--------------|-----------------|-------------|-------------|--------------------/
|  xxxx |  項(xiàng)目         |  PJ         |CX00000000${DATE}|  2650       |  100        |    1

CX00000000${DATE}生成的序列號(hào)類似于:CX0000000120160522 ,CX0000000220160522,CX0000000320160522 ......

序列號(hào)model實(shí)體設(shè)計(jì):

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;

/**
 * 功能描述:序列號(hào)表模型
 *
 * @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;

    /**
     * 流水號(hào)配置模板
     */
    @Column(name = "config_templet", columnDefinition = "VARCHAR")
    private String configTemplet;

    /**
     * 序列號(hào)最大值
     */
    @Column(name = "max_serial", columnDefinition = "VARCHAR")
    private String maxSerial;

    /**
     * 是否自動(dòng)增長(zhǎng)標(biāo)示
     */
    @Column(name = "is_auto_increment", columnDefinition = "VARCHAR")
    private String isAutoIncrement;

    public String getIsAutoIncrement() {
        return isAutoIncrement;
    }

    public void setIsAutoIncrement(String isAutoIncrement) {
        this.isAutoIncrement = isAutoIncrement;
    }

    /**
     * 預(yù)生成流水號(hào)數(shù)量
     */
    @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接口設(shè)計(jì):

package com.evada.de.serialnum.service;

import com.evada.de.serialnum.dto.SystemSerialNumberDTO;

/**
 * 序列號(hào)service接口
 * Created by huangwy on 2015/11/24.
 */
public interface ISerialNumService {

    public SystemSerialNumberDTO find(SystemSerialNumberDTO systemSerialNumberDTO);
        
    public String generateSerialNumberByModelCode(String moduleCode);

    /**
     * 設(shè)置最小值
     * @param value 最小值,要求:大于等于零
     * @return      流水號(hào)生成器實(shí)例
     */
    ISerialNumService setMin(int value);

    /**
     * 設(shè)置最大值
     * @param value 最大值,要求:小于等于Long.MAX_VALUE ( 9223372036854775807 )
     * @return      流水號(hào)生成器實(shí)例
     */
    ISerialNumService setMax(long value);

    /**
     * 設(shè)置預(yù)生成流水號(hào)數(shù)量
     * @param count 預(yù)生成數(shù)量
     * @return      流水號(hào)生成器實(shí)例
     */
    ISerialNumService setPrepare(int count);
}

Service實(shí)現(xiàn):

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();

    /** 流水號(hào)格式化器 */
    private DecimalFormat format = null;

    /** 預(yù)生成鎖 */
    private final ReentrantLock prepareLock = new ReentrantLock();

    /** 最小值 */
    private int min = 0;

    /** 最大值 */
    private long max = 0;

    /** 已生成流水號(hào)(種子) */
    private long seed = min;

    /** 預(yù)生成數(shù)量 */
    private int prepare = 0;

    /** 數(shù)據(jù)庫存儲(chǔ)的當(dāng)前最大序列號(hào) **/
    long maxSerialInt = 0;

    /** 當(dāng)前序列號(hào)是否為個(gè)位數(shù)自增的模式 **/
    private String isAutoIncrement = "0";

    SystemSerialNumberDTO systemSerialNumberDTO =  new SystemSerialNumberDTO();

    /** 預(yù)生成流水號(hào) */
    HashMap<String,List<String>> prepareSerialNumberMap = new HashMap<>();

    /**
     * 查詢單條序列號(hào)配置信息
     * @param systemSerialNumberDTO
     * @return
     */
    @Override
    public SystemSerialNumberDTO find(SystemSerialNumberDTO systemSerialNumberDTO) {
        return serialNumberDAO.find(systemSerialNumberDTO);
    }

    /**
     * 根據(jù)模塊code生成預(yù)數(shù)量的序列號(hào)存放到Map中
     * @param moduleCode 模塊code
     * @return
     */
    @CachePut(value = "serialNumber",key="#moduleCode")
    public List<String> generatePrepareSerialNumbers(String moduleCode){
        //臨時(shí)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{
                    //如果動(dòng)態(tài)數(shù)字長(zhǎng)度大于模板中的長(zhǎng)度 例:模板CF000  maxSerialInt 1000
                    seed = maxSerialInt = 0;
                    //更新數(shù)據(jù),重置maxSerialInt為0
                    systemSerialNumberDTO.setMaxSerial("0");
                    SystemSerialNumber systemSerialNumber = new SystemSerialNumber();
                    BeanUtils.copyProperties(systemSerialNumber,systemSerialNumberDTO);
                    serialNumberRepository.save(systemSerialNumber);
                }
                 //動(dòng)態(tài)數(shù)字生成
                 String formatSerialNum = format.format(seed);

                //動(dòng)態(tài)日期的生成
                if(pattern.contains(SerialNumConstants.DATE_SYMBOL)){
                    String currentDate = DateUtils.format(new Date(),"yyyyMMdd");
                    formatSerialNum = formatSerialNum.replace(SerialNumConstants.DATE_SYMBOL,currentDate);
                }

                resultList.add(formatSerialNum);
            }
            //更新數(shù)據(jù)
            systemSerialNumberDTO.setMaxSerial(maxSerialInt + "");
            SystemSerialNumber systemSerialNumber = new SystemSerialNumber();
            BeanUtils.copyProperties(systemSerialNumber,systemSerialNumberDTO);
            serialNumberRepository.save(systemSerialNumber);
        }finally{
            lock.unlock();
        }
        return resultList;
    }

    /**
     * 根據(jù)模塊code生成序列號(hào)
     * @param moduleCode  模塊code
     * @return  序列號(hào)
     */
    public String generateSerialNumberByModelCode(String moduleCode){

        //預(yù)序列號(hào)加鎖
        prepareLock.lock();
        try{
            //判斷內(nèi)存中是否還有序列號(hào)
            if(null != prepareSerialNumberMap.get(moduleCode) && prepareSerialNumberMap.get(moduleCode).size() > 0){
                //若有,返回第一個(gè),并刪除
                return prepareSerialNumberMap.get(moduleCode).remove(0);
            }
        }finally {
            //預(yù)序列號(hào)解鎖
            prepareLock.unlock();
        }
        systemSerialNumberDTO = new SystemSerialNumberDTO();
        systemSerialNumberDTO.setModuleCode(moduleCode);
        systemSerialNumberDTO = serialNumberDAO.find(systemSerialNumberDTO);
        prepare = Integer.parseInt(systemSerialNumberDTO.getPreMaxNum().trim());//預(yù)生成流水號(hào)數(shù)量
        pattern = systemSerialNumberDTO.getConfigTemplet().trim();//配置模板
        String maxSerial = systemSerialNumberDTO.getMaxSerial().trim(); //存儲(chǔ)當(dāng)前最大值
        isAutoIncrement = systemSerialNumberDTO.getIsAutoIncrement().trim();
        maxSerialInt = Long.parseLong(maxSerial.trim());//數(shù)據(jù)庫存儲(chǔ)的最大序列號(hào)
        max = this.counter(pattern,'0') + 1;//根據(jù)模板判斷當(dāng)前序列號(hào)數(shù)字的最大值
        if(isAutoIncrement.equals("1")){
            pattern = pattern.replace("0","#");
        }
        format = new DecimalFormat(pattern);
        //生成預(yù)序列號(hào),存到緩存中
        List<String> resultList = generatePrepareSerialNumbers(moduleCode);
        prepareLock.lock();
        try {
            prepareSerialNumberMap.put(moduleCode, resultList);
            return prepareSerialNumberMap.get(moduleCode).remove(0);
        } finally {
            prepareLock.unlock();
        }
    }

    /**
     * 設(shè)置最小值
     *
     * @param value 最小值,要求:大于等于零
     * @return 流水號(hào)生成器實(shí)例
     */
    public ISerialNumService setMin(int value) {
        lock.lock();
        try {
            this.min = value;
        }finally {
            lock.unlock();
        }
        return this;
    }

    /**
     * 最大值
     *
     * @param value 最大值,要求:小于等于Long.MAX_VALUE ( 9223372036854775807 )
     * @return 流水號(hào)生成器實(shí)例
     */
    public ISerialNumService setMax(long value) {
        lock.lock();
        try {
            this.max = value;
        }finally {
            lock.unlock();
        }
        return this;
    }

    /**
     * 設(shè)置預(yù)生成流水號(hào)數(shù)量
     * @param count 預(yù)生成數(shù)量
     * @return      流水號(hào)生成器實(shí)例
     */
    public ISerialNumService setPrepare(int count) {
        lock.lock();
        try {
            this.prepare = count;
        }finally {
            lock.unlock();
        }
        return this;
    }

    /**
     * 統(tǒng)計(jì)某一個(gè)字符出現(xiàn)的次數(shù)
     * @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;
    }

}

讀書感悟

  • 生活壞到一定程度就會(huì)好起來,因?yàn)樗鼰o法更壞。努力過后,才知道許多事情,堅(jiān)持堅(jiān)持,就過來了。
  • 有些煩惱,丟掉了,才有云淡風(fēng)輕的機(jī)會(huì)。
  • 當(dāng)一個(gè)胖紙沒有什么不好,最起碼可以溫暖其他的人。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,769評(píng)論 25 709
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,055評(píng)論 4 61
  • 作業(yè)1:好好回想一下,小時(shí)候呆呆看過什么。挑選一個(gè)印象或一幅畫面寫出來。 小時(shí)候常常會(huì)坐在窗戶邊往外看風(fēng)景。老家的...
    王姝嬈閱讀 259評(píng)論 4 0
  • ERROR: Error during SonarQube Scanner execution ERROR: Fa...
    azhao閱讀 7,162評(píng)論 0 1

友情鏈接更多精彩內(nèi)容