基于mysql的单据号生成(前缀+日期+自增id+后缀)
介绍
本次采用mysql处理,性能不是很好,对于高并发有要求的建议不要采用
公司一个小项目,需要生成一个单据号,格式为: 日期 + 每日重新自增号,自己考虑了一下每日自增需要考虑并发和持久问题,两种数据库redis和mysql由于项目较小,所以没有redis因为这个增加一个redis好像有点不值得,所以采用mysql作为持久化处理,一下思路也是借鉴了网上的许多想法
源码
源码查看规则
源码位置: blog-study:module-utils:work-no
欢迎大家随时批评指正
思路
- 首先根据需求,需要这样一个格式: 20200101 + 00001 的单据号
- 分析日期只需要获取当前时间格式化即可简单,
自增号:
1)需要考虑每日重新开始
2)宕机数据可以持久化(存库即可)
3)并发获取单据号唯一
以上四个问题只需要考虑 每日重新开始计数和并发问题 - 每日重新开始,在数据库中加一个时间标志,每次获取都与当前时间做对比然后做相应处理, 并发问题我能想到的就是加锁(编程锁或者数据库锁,我这里采用lock锁)
- 以上基本可以解决项目问题了,然后思考是否可以用于其它业务号的生成
1)于是想可以增加业务标识、前缀和后缀来满足不同业务,其中业务标识不参与单据号组成只是标识某一类单据号,这样可以保证不同业务可以使用相同的单据号,前缀是根据业务需要标识业务单据号的参与单据号生成, 后缀的作用是干扰随机数,可以尽可能防止他人直接看出来业务单据每日的生成数量
格式: GX + 20200101 + 00001 + 23
2) 每日重新开始计数,是否可以改为可选择的,每月重新计数,每年重新计数,或者一直不需要重新计数 - 思考是否可以封装为组件,引入依赖直接就可以使用,想了一下自己目前还不具备这种能力吧,以后还要多学多了解,也希望大家可以多多指点,小子将不胜感激。
功能介绍
- 数据库结构(使用实体类代替)
/**
* 业务唯一标识(只是作为唯一标识,并不参与单据号的生成)
*/
private String workNo;
/**
* 业务码前缀
*/
private String prefix;
/**
* 序号
*/
private Integer serialNum;
/**
* 序号长度
*/
private Integer serialLength;
/**
* 序号之后的随机数长度(干扰串)
*/
private Integer randomLength;
/**
* 重置模式(0:无需重置,1: 按天重置, 2: 按月重置, 3: 按年重置)
*/
private Integer restType;
/**
* 重置时间(检测是否需要重置使用)
*/
private LocalDate restTime;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
/**
* 备注
*/
private String remark;
- 业务号格式使用枚举规定好,每次启动项目自动识别处理
/**
* 为了保证每次生成的单号一致性,所以初始化作为第一次初始化成功后不再修改
*/
ORDER_NO("XG", "XG", 5, 2, 1, "小郭测试单据号");/**
* 业务唯一标识(只是作为唯一标识,并不参与单据号的生成)
*/
private String workNo;
/**
* 业务码前缀
*/
private String prefix;
/**
* 序号长度
*/
private Integer serialLength;
/**
* 序号之后的随机数长度(干扰串)0代表没有干扰
*/
private Integer randomLength;
/**
* 重置模式(0:无需重置,1: 按天重置, 2: 按月重置, 3: 按年重置)
*/
private Integer restType;
/**
* 备注
*/
private String remark;
项目启动初始化:
@Configuration
public class WorkNoInit implements ApplicationRunner {
@Autowired
private WorkNoDao workNoDao;/**
* 单据号初始化
*
* @param args
* @throws Exception
*/
@Override
public void run(ApplicationArguments args) throws Exception {
WorkNoInfo workNoInfo = null;
for (WorkInfoEnum workInfoEnum : WorkInfoEnum.values()) {
workNoInfo = new WorkNoInfo(workInfoEnum);
//查询是否已有该业务单据号(没有初始化)
if (workNoDao.isHaveWorkNo(workNoInfo.getWorkNo()) == 0) {
workNoDao.addOrderNumInfo(workNoInfo);
}
}
}
}
- 获取单据号:
@Component
public class WorkNoService {@Autowired
private WorkNoDao workNoDao;private static Lock lock = new ReentrantLock();
/**
* 获取业务序列码
*
* 流程:
* 1. 加锁保证线程安全
* 2. 查询数据库中的业务信息
* 3. 判断重置模式修改数据库中相应的数据
* 4. 返回业务码
*
* @param workInfoEnum 枚举
* @return 业务序列码
*/
public String getOrderNo(WorkInfoEnum workInfoEnum) {
lock.lock();
LocalDateTime localDateTime = LocalDateTime.now();
try {
WorkNoInfo workNoInfo = workNoDao.queryByWorkNo(workInfoEnum.getWorkNo());
if (workNoInfo == null) {
throw new ErrorCodeException(500, "查询生成业务码基础数据失败!");
}
//序号
Integer serialNum = workNoInfo.getSerialNum();
//判断是否需要重置序号
if (workNoInfo.getRestType() == 1) {
String dayTime = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String dataBaseTime = workNoInfo.getRestTime().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
if (!dataBaseTime.equals(dayTime)) {
serialNum = 0;
}
} else if (workNoInfo.getRestType() == 2) {
String monthTime = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMM"));
String dataBaseTime = workNoInfo.getRestTime().format(DateTimeFormatter.ofPattern("yyyyMM"));
if (!dataBaseTime.equals(monthTime)) {
serialNum = 0;
}
} else if (workNoInfo.getRestType() == 3) {
String yearTime = localDateTime.format(DateTimeFormatter.ofPattern("yyyy"));
String dataBaseTime = workNoInfo.getRestTime().format(DateTimeFormatter.ofPattern("yyyy"));
if (!dataBaseTime.equals(yearTime)) {
serialNum = 0;
}
}
workNoInfo.setSerialNum(serialNum + 1);
workNoInfo.setRestTime(localDateTime.toLocalDate());
//更新数据库
workNoDao.updateOrderNo(workNoInfo);
return workNoInfo.getNo();
} catch (Exception e) {
throw new ErrorCodeException(500, "生成业务码失败" + e.getMessage());
} finally {
lock.unlock();
}
}}
基于mysql的单据号生成(前缀+日期+自增id+后缀)的更多相关文章
- 基于redis的订单号生成方案
目前,比较火的nosql数据库,如MongoDB,Redis,Riak都提供了类似incr原子行操作. 下面是PHP版的一种实现方式: <?php /** * 基于Redis的全局订单号id * ...
- mysql 插入数据后返回当前的自增ID方法
存储过程的写法: mysql>create procedure test( ->in username varchar(50), ->in password varchar(50), ...
- mysql 多主多从配置,自增id解决方案
MySQL两主(多主)多从架构配置 一.角色划分 1.MySQL数据库规划 我现在的环境是:zhdy04和zhdy05已经做好了主主架构配置,现在需要的是把两台或者多台从服务器与主一一同步. 主机名 ...
- MySQL获得指定数据表中auto_increment自增id值的方法及实例
http://kb.cnblogs.com/a/2357592/很多情况下,我们要提前用到当前某个表的auto_increment自增列id,可以通过执行sql语句来查询到这个id值. show ta ...
- .NET 6 在小并发下如何生成唯一单据号
一.场景介绍 小并发下要解决生成单据号的问题,会碰到哪些问题呢?,接下来让我们一探究竟[这是小并发的解决方案,大家有更好的做好可以一起讨论分享]. 之所以叫小并发:是因为确实是小并发场景的应用模式,一 ...
- MySQL 使用自增ID主键和UUID 作为主键的优劣比较详细过程(从百万到千万表记录测试)
测试缘由 一个开发同事做了一个框架,里面主键是uuid,我跟他建议说mysql不要用uuid用自增主键,自增主键效率高,他说不一定高,我说innodb的索引特性导致了自增id做主键是效率最好的,为了拿 ...
- 45 MySQL自增id
45 MySQL自增id 表定义自增id 说到自增id,前面提到mysql的自增id不连续,当表定义的自增值达到上限后的逻辑是:再申请下一个id时,得到的值保持不变 ; insert into t v ...
- oracle生成单据号
--创建单据号存放表 CREATE TABLE BU_TAB( DOC_NUM NUMBER --生成的单据号 ); --单据号 create table cux_doc_num( tab ), -- ...
- 用SQL存储过程生成唯一单据号
用SQL存储过程生成唯一单据号 在一些系统中,经理要生成单据号,为了不使多台客户端生成的单据号重复,一般要在服务端生成这种流水号,本文是在数据库中生成流水号,并且可以生成多种类型的单据号(比如 ...
随机推荐
- Scheduler
先看看文档对于Scheduler的作用介绍 https://code4craft.gitbooks.io/webmagic-in-action/content/zh/posts/ch1-overvie ...
- InetlliJ IDEA的快捷键及各种配置
在IDEA中新建Maven工程,之后再新建一个Maven模块,此时在模块中的src/main/java中右键新建Java文件时,发现只能新建普通文件,不能新建Java类了,怎么回事,和eclipse的 ...
- C# “不支持给定路径的格式”异常处理
问题背景 无聊研究了一下怎么发送邮件(包含附件),但发现附带的文件路径除了报错就是报错,不知道为什么. 用了不下好几种方式,比如 var x = "E:\\Git\\cmd\\git.exe ...
- Vue中通过属性绑定为元素绑定style行内样式
1.直接在元素上通过:style的形式,书写样式对象 2.将样式对象定义在data中,并直接引用到:style中 3.在:style中通过数组,引用多个data上的样式对象
- CF1228——记一次和紫名失之交臂的CF
CF1228--记一次和紫名失之交臂的CF 第无数次和暴涨失之交臂 A 题目大意给定\(l,r\)输出任意一个\(l,r\)之间的每一位都不同的数 \((l <= r <= 10^5)\) ...
- spring的69个问题
1.什么是Spring? Spring是一个开源的Java EE开发框架.Spring框架的核心功能可以应用在任何Java应用程序中,但对Java EE平台上的Web应用程序有更好的扩展性.Sprin ...
- python实现单词本功能
#实现简单的单词本:# 可以添加单词和词义,当所添加的单词已经存在 让用户知道:# 查找单词,单词不存在时,让用户知道# 删除单词,当删除的单词不存在时,让用户知道# 以上功能无限制操作,直到用户输入 ...
- jsp页面出错 Cannot call sendRedirect() after the response has been committed
sendRedirect()不能多次调用,检查下代码
- python 批量生成xml标记文件(连通域坐标分割)
#!/usr/bin/python # -*- coding=utf-8 -*- # author : Manuel # date: 2019-05-15 from xml.etree import ...
- dWebpack编译速度优化实战
当你的应用的规模还很小时,你可能不会在乎Webpack的编译速度,无论使用3.X还是4.X版本,它都足够快,或者说至少没让你等得不耐烦.但随着业务的增多,嗖嗖嗖一下项目就有上百个组件了,也是件很简单的 ...