带事务的锁引发的惨案

业务需要生成一个有序的编号

代码:

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> saveEntity(ProjectApprovalDTO dto) {
        ProjectApproval entity = convert.toEntity(dto);
        // 判断行政区划和行政区域是否全部为中文
        if (!Validator.isChinese(dto.getDirection()) || !Validator.isChinese(dto.getRegion())) {
            throw new GlobalBadRequestException("");
        }

        StringBuilder sb = new StringBuilder();
        // 取行政区划 行政区域 拼音首字母
        sb.append(StrUtil.swapCase(PinyinUtil.getFirstLetter(dto.getDirection(), StrUtil.EMPTY)))
            .append(StrUtil.swapCase(PinyinUtil.getFirstLetter(dto.getRegion(), StrUtil.EMPTY))).append(StringPool.DASH)
            .append(DateUtil.format(new Date(), DatePattern.SIMPLE_MONTH_PATTERN)).append(StringPool.DASH);

        RLock lock = redisson.getLock(LOCK_KEY);
        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    ProjectApproval project = projectApprovalMapper.findLastedCode(sb.toString());
                    NumberFormat formatter = NumberFormat.getNumberInstance();
                    formatter.setMinimumIntegerDigits(4);
                    formatter.setGroupingUsed(false);
                    long num = 1;
                    if (!ObjectUtil.isEmpty(project)) {
                        String[] split = project.getCode().split(StringPool.DASH);
                        String s = split[split.length - 1];
                        num = Integer.parseInt(s) + 1;
                    }
                    sb.append(formatter.format(num));
                    entity.setCode(sb.toString());
                    save(entity);
                    if (!StrUtil.isEmpty(dto.getNicheId())) {
                        nicheService.updateById(Niche.Builder.aNiche().id(dto.getNicheId()).projectId(entity.getId())
                            .enabled(String.valueOf(StateEnum.DISABLE.getValue())).build());
                    }
                    return MapUtil.<String, String>builder().put(BUSINESS_KEY, entity.getId())
                        .put(SERVICE_NAME, ServiceNameEnum.projectService.getValue()).build();
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 只能释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

然后生成的code总是有重复的,加日志加日志

longcoming-oa: 2024-07-27 12:53:13.595  INFO 141708 [http-nio-7077-exec-32] c.l.oa.service.impl.ProjectServiceImpl : thread: 300, getcode: DBHLJ-202407-1873
longcoming-oa: 2024-07-27 12:53:13.595  INFO 141708 [http-nio-7077-exec-32] c.l.oa.service.impl.ProjectServiceImpl : thread: 300, setcode: DBHLJ-202407-1874
longcoming-oa: 2024-07-27 12:53:13.619  INFO 141708 [http-nio-7077-exec-32] c.l.oa.service.impl.ProjectServiceImpl : unLock: 300 
longcoming-oa: 2024-07-27 12:53:13.624  INFO 141708 [http-nio-7077-exec-35] c.l.oa.service.impl.ProjectServiceImpl : getLock: 305
longcoming-oa: 2024-07-27 12:53:13.632  INFO 141708 [http-nio-7077-exec-35] c.l.oa.service.impl.ProjectServiceImpl : thread: 305, getcode: DBHLJ-202407-1874
longcoming-oa: 2024-07-27 12:53:13.632  INFO 141708 [http-nio-7077-exec-35] c.l.oa.service.impl.ProjectServiceImpl : thread: 305, setcode: DBHLJ-202407-1875
longcoming-oa: 2024-07-27 12:53:13.652  INFO 141708 [http-nio-7077-exec-35] c.l.oa.service.impl.ProjectServiceImpl : unLock: 305 
longcoming-oa: 2024-07-27 12:53:13.656  INFO 141708 [http-nio-7077-exec-27] c.l.oa.service.impl.ProjectServiceImpl : getLock: 284
longcoming-oa: 2024-07-27 12:53:13.667  INFO 141708 [http-nio-7077-exec-27] c.l.oa.service.impl.ProjectServiceImpl : thread: 284, getcode: DBHLJ-202407-1874
longcoming-oa: 2024-07-27 12:53:13.667  INFO 141708 [http-nio-7077-exec-27] c.l.oa.service.impl.ProjectServiceImpl : thread: 284, setcode: DBHLJ-202407-1875
longcoming-oa: 2024-07-27 12:53:13.696  INFO 141708 [http-nio-7077-exec-27] c.l.oa.service.impl.ProjectServiceImpl : unLock: 284 
longcoming-oa: 2024-07-27 12:53:13.705  INFO 141708 [http-nio-7077-exec-29] c.l.oa.service.impl.ProjectServiceImpl : getLock: 296
longcoming-oa: 2024-07-27 12:53:13.716  INFO 141708 [http-nio-7077-exec-29] c.l.oa.service.impl.ProjectServiceImpl : thread: 296, getcode: DBHLJ-202407-1875
longcoming-oa: 2024-07-27 12:53:13.716  INFO 141708 [http-nio-7077-exec-29] c.l.oa.service.impl.ProjectServiceImpl : thread: 296, setcode: DBHLJ-202407-1876

锁是没有问题的,有理由怀疑是释放锁的时候事务还没有提交导致获取了重复的编号

改改改

    @Override
    public Map<String, String> saveEntity(ProjectApprovalDTO dto) {
        // 判断行政区划和行政区域是否全部为中文
        if (!Validator.isChinese(dto.getDirection()) || !Validator.isChinese(dto.getRegion())) {
            throw new GlobalBadRequestException("");
        }

        RLock lock = redisson.getLock("generateCode");
        boolean tryLock;
        try {
            tryLock = lock.tryLock(5, TimeUnit.SECONDS);
            if (!tryLock) {
                throw new GlobalBadRequestException("");
            }
            return generateCodeAndSave(dto);
        } catch (InterruptedException e) {
            log.error("ProjectServiceImpl occurs error", e);
            throw new GlobalBadRequestException(LOCK_FAILED);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> generateCodeAndSave(ProjectApprovalDTO dto) {
        ProjectApproval projectApproval = convert.toEntity(dto);
        StringBuilder sb = new StringBuilder();
        // 取行政区划 行政区域 拼音首字母
        sb.append(StrUtil.swapCase(PinyinUtil.getFirstLetter(projectApproval.getDirection(), StrUtil.EMPTY)))
            .append(StrUtil.swapCase(PinyinUtil.getFirstLetter(projectApproval.getRegion(), StrUtil.EMPTY)))
            .append(StringPool.DASH).append(DateUtil.format(new Date(), DatePattern.SIMPLE_MONTH_PATTERN))
            .append(StringPool.DASH);

        ProjectApproval projectMaxCode = projectApprovalMapper.findLastedCode(sb.toString());
        NumberFormat formatter = NumberFormat.getNumberInstance();
        formatter.setMinimumIntegerDigits(4);
        formatter.setGroupingUsed(false);
        long num = 1;
        if (!ObjectUtil.isEmpty(projectMaxCode)) {
            String[] split = projectMaxCode.getCode().split(StringPool.DASH);
            String s = split[split.length - 1];
            num += Integer.parseInt(s);
        }
        sb.append(formatter.format(num));
        projectApproval.setCode(sb.toString());
        if (save(projectApproval) && !StrUtil.isEmpty(dto.getNicheId())) {
            nicheService.updateById(Niche.Builder.aNiche().id(dto.getNicheId()).projectId(projectApproval.getId())
                .enabled(String.valueOf(StateEnum.DISABLE.getValue())).build());
        }
        return MapUtil.<String, String>builder().put(BUSINESS_KEY, projectApproval.getId())
            .put(SERVICE_NAME, ServiceNameEnum.projectService.getValue()).build();
    }

问题解决啦

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值