编号根据规则自增生成,及spring事务和锁

1、 背景

需要根据一些规则来生成自增编号,比如:95JS0001,950002
95JS是固定的,而后缀的0001的长度也是可配置的,因为有一张表来进行维护


CREATE TABLE `number_control` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `number_category` varchar(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '编号类别',
  `number_type` char(2) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '0为微机编号,1为人工编号',
  `is_department_number` char(2) COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '是否开启单位号,默认开启',
  `project_num` char(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '项目号',
  `is_year` char(2) COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '是否开启年份,默认开启',
  `is_month` char(2) COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '是否开启月份,默认开启',
  `divide_sign` char(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分隔符号',
  `digit` int DEFAULT '1' COMMENT '位数,默认为1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2181 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='编号管理表';

为了方便编号的查询,于是又维护另一张表来存储生成编号的进度

CREATE TABLE `generate_number` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `prefix` varchar(100) DEFAULT NULL COMMENT '前缀,根据编号规则生成的前缀',
  `number` bigint DEFAULT '1' COMMENT '记录生成的号码',
  `number_category` char(2) DEFAULT NULL COMMENT '编号类别',
  `digit` int DEFAULT NULL COMMENT '位数'
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='记录编号生成表';

因为要求编号必须连续且自增,所以必然要使用到锁,这里使用的是ReentrantLock,因为涉及到多张表的查看及更新,也用到了事务,因为锁和事务,遇到了一些问题

2、解决

先说解决方案,遇到的问题放在第三点,想看的可以看看
首先参考这篇章
https://blog.csdn.net/zzhongcy/article/details/103583008
写了一个切面,但要注意的是必须要加@order注解,否则锁不住。
另外要把切面的catch去掉,将异常抛出,否则事务会失效

@Component
@Aspect
@Order(1)
public class LockAspect {

    private  final Lock lock = new ReentrantLock();

    @Pointcut("@annotation(Servicelock)")
    public void lockAspect() {}

    @Around("lockAspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        lock.lock();
        Object obj;
        //catch会导致事务失效,所以抛出异常
        try {
            obj = joinPoint.proceed();
        }
        finally {
            lock.unlock();
        }
        return obj;
    }
}

3、思路及解决步骤

3.1、首先我想的是直接写一个工具类,让其他人调用生成编号的

  public String getNumber(Long reportId, String category) {
        NumberControl numberControl = numberControlManage.selectByReportIdAndNumberCategory(reportId, category);
        if (numberControl != null) {
            //人工编号返回null
            if (ConstantPool.ONE.toString().equals(numberControl.getNumberType())) {
                return null;
            }

            Integer digit = numberControl.getDigit();
            String predix = generatePrefix(numberControl);

           
            lock.lock();
            try {
                //去生成编号中查询一下
                GenerateNumber generateNumber = generateNumberManage.getByPrefixAndCategory(predix, category, digit);
                //说明没有
                if (generateNumber == null) {
                    generateNumber = new GenerateNumber();
                    generateNumber.setPrefix(predix);
                    generateNumber.setNumberCategory(category);
                    generateNumber.setDigit(digit);
                    generateNumberManage.save(generateNumber);
                    return predix + formatNumber(digit, 1);
                } else {
                    Long number = generateNumber.getNumber() + 1;
                    generateNumber.setNumber(number);
                    generateNumberManage.updateById(generateNumber);
                    return predix + formatNumber(digit, number.intValue());
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            } finally {
                lock.unlock();
            }

        }
        return null;
    }

这里直接使用ReentrantLock锁住,但是当上层方法加上事务的时候,会导致锁失效。
简单来说就是:事务还未提交的时候,锁已经释放了

   @Transactional(rollbackFor = Exception.class)
    public R<Boolean> copy(EntrustCopyDto dto) {
        EntrustSamples byId = entrustSamplesManage.getById(dto.getId());
        //将id置为空
        byId.setId(null);

        String number;
        if (ConstantPool.YES.toString().equals(dto.getPreEntrust())) {
        //这里走完锁就会被释放掉,而事务还未被提交
            number = numberControlService.getNumber(byId.getCheckProjectId(), NumberCategoryEnum.PREENTRUST.getCode());
        } else {
            number = numberControlService.getNumber(byId.getCheckProjectId(), NumberCategoryEnum.ENTRUST.getCode());
        }
        byId.setCommissionNumber(number);
        return R.ok(entrustSamplesManage.save(byId));
    }

3.2、发现上面这样写不行,改成切面方式

https://blog.csdn.net/zzhongcy/article/details/103583008
当然也有其他方式,可参考
https://blog.csdn.net/small_love/article/details/127263276
按理说,这样应该可以了,但是发现还是锁不住

3.3、添加@order

因为看文章提到了@order,抱着死马当活马医的心态加上去了,结果竟然好了。不信邪,又去掉@order压测,加上@order压测尝试了好几遍,发现加上去之后确实可以。
于是之后就开始查找原因,之前看文章说@Transactional的默认优先级就是Integer.MAX_VALUE,数字越大优先级越低,想着默认的怎么也不至于比它优先级还低吧。。。。
结果还真是一样低。
首先进去@order注解,发现是通过这个类来进行比较优先级别的
在这里插入图片描述
然后进去该类,翻译
在这里插入图片描述
大体就是这样了,折腾了很久。。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值