基于mysql的单据号生成(前缀+日期+自增id+后缀)

介绍

本次采用mysql处理,性能不是很好,对于高并发有要求的建议不要采用
公司一个小项目,需要生成一个单据号,格式为: 日期 + 每日重新自增号,自己考虑了一下每日自增需要考虑并发和持久问题,两种数据库redis和mysql由于项目较小,所以没有redis因为这个增加一个redis好像有点不值得,所以采用mysql作为持久化处理,一下思路也是借鉴了网上的许多想法

源码

源码查看规则
源码位置: blog-study:module-utils:work-no

欢迎大家随时批评指正

思路

  1. 首先根据需求,需要这样一个格式: 20200101 + 00001 的单据号
  2. 分析日期只需要获取当前时间格式化即可简单,
    自增号:
    1)需要考虑每日重新开始
    2)宕机数据可以持久化(存库即可)
    3)并发获取单据号唯一
    以上四个问题只需要考虑 每日重新开始计数和并发问题
  3. 每日重新开始,在数据库中加一个时间标志,每次获取都与当前时间做对比然后做相应处理, 并发问题我能想到的就是加锁(编程锁或者数据库锁,我这里采用lock锁)
  4. 以上基本可以解决项目问题了,然后思考是否可以用于其它业务号的生成
    1)于是想可以增加业务标识、前缀和后缀来满足不同业务,其中业务标识不参与单据号组成只是标识某一类单据号,这样可以保证不同业务可以使用相同的单据号,前缀是根据业务需要标识业务单据号的参与单据号生成, 后缀的作用是干扰随机数,可以尽可能防止他人直接看出来业务单据每日的生成数量
    格式: GX + 20200101 + 00001 + 23
    2) 每日重新开始计数,是否可以改为可选择的,每月重新计数,每年重新计数,或者一直不需要重新计数
  5. 思考是否可以封装为组件,引入依赖直接就可以使用,想了一下自己目前还不具备这种能力吧,以后还要多学多了解,也希望大家可以多多指点,小子将不胜感激。

功能介绍

  1. 数据库结构(使用实体类代替)
/**     * 业务唯一标识(只是作为唯一标识,并不参与单据号的生成)     */    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;
  1. 业务号格式使用枚举规定好,每次启动项目自动识别处理
/**     * 为了保证每次生成的单号一致性,所以初始化作为第一次初始化成功后不再修改     */    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;

项目启动初始化:

@Configurationpublic 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);            }        }    }}
  1. 获取单据号:
@Componentpublic 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();        }    }

}

原文地址:https://www.cnblogs.com/chunyun/p/12128430.html

时间: 2024-10-11 17:57:40

基于mysql的单据号生成(前缀+日期+自增id+后缀)的相关文章

分布式系统中的必备良药 —— 全局唯一单据号生成

阅读目录 单据号是指什么 和唯一ID的不同是什么 为什么需要全局唯一单据号生成程序 实现的方式有哪些 笔者推荐的方式 结语 一.单据号是指什么 我们作为一个软件系统,肯定到处充满着各种单据,也必然需要有各种单据号与之对应.比如:电商行业的订单号.支付流水号.退款单号等等.SCM的采购单号.进货单号.出货单号.盘点单号等.在一个企业内部或者一个2C的平台,无法避免的需要通过某个单据号来进行沟通.所以一个好的单据号必然是便于沟通的,简单来说优先级就是 好记 > 好输入 > 好看,当然也是越短越好.

mysql清空表数据后如何让自增ID仍从1开始

mysql清空表数据后如何让自增ID仍从1开始?也就是说如何重排auto_increment 清空表时使用truncate命令,而不用delete命令 mysql> truncate test; 使用truncate命令的好处: 1).速度快 2).可以对自增ID进行重排,使自增ID仍从1开始计算   原文地址:https://www.cnblogs.com/superboblogs/p/9445318.html

系统单据号生成存储过程

CREATE PROCEDURE [dbo].[GeneralDocumentNoByBatch]    @preFix nvarchar(256),@GenNum int ,@outCode varchar(256) outputASBEGIN    -- SET NOCOUNT ON added to prevent extra result sets from    -- interfering with SELECT statements.    declare @docNo nvarc

mysql 插入数据后返回当前的自增ID方法

存储过程的写法: mysql>create procedure test( ->in username varchar(50), ->in password varchar(50), ->out userid int) ->begin ->set @sql=concat("insert into user(`username`,`password`) values(' ",username,"' ,' ",password,&qu

MySQL获得指定数据表中auto_increment自增id值的方法及实例

http://kb.cnblogs.com/a/2357592/很多情况下,我们要提前用到当前某个表的auto_increment自增列id,可以通过执行sql语句来查询到这个id值. show table status where name=’表名’ 或者 show table status like ‘表名’ 然后从查询到的结果集中获得auto_increment的值 代码实例:<?php mysql_connect("localhost","root",

MySQL 使用自增ID主键和UUID 作为主键的优劣比较详细过程(从百万到千万表记录测试)

测试缘由 一个开发同事做了一个框架,里面主键是uuid,我跟他建议说mysql不要用uuid用自增主键,自增主键效率高,他说不一定高,我说innodb的索引特性导致了自增id做主键是效率最好的,为了拿实际的案例来说服他,所以准备做一个详细的测试.   作为互联网公司,一定有用户表,而且用户表UC_USER基本会有百万记录,所以在这个表基础上准测试数据来进行测试.            测试过程是目前我想到的多方位的常用的几种类型的sql进行测试,当然可能不太完善,欢迎大家留言提出更加完善的测试方

MySQL查看自增ID和表DDL等信息

MySQL查看自增ID和表DDL等信息技术 maybe yes 发表于2015-01-24 21:50 原文链接 : http://blog.lmlphp.com/archives/69  来自 : LMLPHP后院 MySQL 查看数据库表相关信息和自增 ID 方法如下: 方法一 mysql> show create table table_name\G; *************************** 1. row ***************************       

.Net常用技巧_生成单据号

自己用的,没整理,代码比较乱,请不要学我. using System; using System.Collections.Generic; using System.Text; using EXDataControl; using System.Data; using System.Data.SqlClient; using Utility; namespace MyTool { public class CreateDocNo { /// <summary> /// 获取单号 /// <

用SQL存储过程生成唯一单据号

在一些系统中,经理要生成单据号,为了不使多台客户端生成的单据号重复,一般要在服务端生成这种流水号,本文是在数据库中生成流水号,并且可以生成多种类型的单据号(比如销售单据号,盘点单据号,进货单据号等),利用数据库锁的原理,先看一下SQL语句: CREATE PROC [dbo].[getlsh]     @lx VARCHAR(6) ,     @lsh VARCHAR(30) OUTPUT AS     BEGIN  --启动事务处理         [email protected]_poin