提出问题
如何使用java生成流水号,同时支持可配置和高并发?
解决问题
假设你们项目已经整合缓存技术
假如你有一定的java基础
假如……
下面的代码实现的是一个支持高并发,可配置,效率高的流水号生成器,可同时为一个项目的多个模块使用,流水号支持缓存,即每次会预先生成一定数量的流水号存放在缓存中,需要的时候,优先到缓存中去,缓存中的序列号使用完之后,重新生成一定数量的流水号放到缓存中,如此循环,提高效率……
同时,该流水号生成器是线程安全的,使用线程锁进行保护,已经真正的投入到项目中使用……
数据库表设计
[code]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"
)
说明:
[code]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中配置一条记录:
[code]| 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实体设计:
[code]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接口设计:
[code]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实现:
[code]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);
//动态日期的生成
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;
}
}
以上就是java之流水号生成器的内容。