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

开心一笑

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;

}

}

读书感悟

生活坏到一定程度就会好起来,因为它无法更坏。努力过后,才知道许多事情,坚持坚持,就过来了。

有些烦恼,丢掉了,才有云淡风轻的机会。

当一个胖纸没有什么不好,最起码可以温暖其他的人。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值