开心一笑
视频教程
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;}}
读书感悟
- 生活坏到一定程度就会好起来,因为它无法更坏。努力过后,才知道许多事情,坚持坚持,就过来了。
- 有些烦恼,丢掉了,才有云淡风轻的机会。
- 当一个胖纸没有什么不好,最起码可以温暖其他的人。
其他
如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎转载,点赞,顶,欢迎留下宝贵的意见,多谢支持!


















