实现类伪代码如下
@Transactional(rollbackFor = Exception.class) public String getSeq(int id) { SequenceMapper sequenceMapper = SpringContextUtis.getBean("sequenceMapper", SequenceMapper.class); // 获取当前时间并格式化 yyyyMMddHHmm String formatDate = dateFormatter.format(now); // 取出数据库中的序列化对象 // 获取上一次的序列对象 Sequence sequence = sequenceMapper.selectOne(Wrappers.<Sequence>lambdaQuery().eq(Sequence::getId, 1)); // 如果数据库中存在 if (sequence != null) { // 时间相同则将排序号加1 if (sequence中日期字符串 == formatDate) { int last = sequence.last += 1; sequence.last = last; } else { // 时间不同 则直接创建一个 当前时间 + 排序号的 序列 sequence.formatDate = formatDate; sequence.last = last; } } else { // 如果这种类型的序列从未创建过 则创建一个 当前时间 + 排序号的序列 sequence = new Sequence(); sequence.id = 1; sequence.formatDate = formatDate; sequence.last = formatDate + "01"; // 插入到数据库 if(sequenceMapper.insert(sequence) > 0) return sequence.format + last; else throw new RuntimeException(); } // 保存到数据库中 并记录上一次的 时间 和 排序号 if(sequenceMapper.update(sequence, Wrappers.<Sequence>lambdaUpdate().eq(Sequence::getId(), sequence.id)) > 0) return sequence.format + last; else throw new RuntimeException();
调用类伪代码如下
public String getUserSeq(){ // 这里使用 redisson 作为分布式锁 RedissonClient redissonClient = SpringContextUtis.getBean("redissonClient", RedissonClient.class); RLock lock = redissonClient.getLock("sequence"); String value = null; // 只有真正获取到值才停止 while(value == null) { lock.lock(); try { value = sequenceGenerate.getSeq(1); }catch(Exception exception){ log.error("获取失败,继续获取"); }finally { lock.unlock(); } } return value; }
代码基本就上面那些,在本地单机环境下没出现过 序列字符串 重复的问题;在集群环境下时而出现,按照我的理解来说,在创建时已经使用redisson锁住了,应该是不会出现数据脏读的问题,这样应该是不会重复的。求各位大佬帮忙解答一下,十分感谢。
你这样还是会有脏数据的。
因为在 unlock 的时候,事务是没有提交的,此时数据还没写进去,其它进程又进来读数据了。
换种说法,目前你的做法,事务应该是整个线程结束后才被提交,而不是执行完 getSeq 就提交。
你这种情况我之前遇到,单纯的使用 @Transactional(rollbackFor = Exception.class)
,不论嵌套再多方法,使用的实际上都是同一个事务,当你打开一个事物的时候。
试想一下,当你打开同一个事务时,你如何让程序知道去主动提交事务呢?spring 框架并没有给你获取当前线程事务,手动提交的接口。
因此唯一的可能就是当线程结束后,spring 才知道此时要提交事务了。
@Transactional(rollbackFor = Exception.class)
改成:
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
让方法结束时,强行提交到数据库。
Propagation.REQUIRES_NEW
:含义是,参数表示当前方法需要在新的事务中运行,加上之后,才能让方法结束后就立即提交事务。
更加彻底的方法是使用 TransactionTemplate
新开一个事务,这样掌控起来更加容易和便于理解。
@Autowired TransactionTemplate template; public void test() { template.execute(status -> { // 数据库更新 return null; }); }
问题内容: Oracle是否具有内置功能来根据其各个组成部分(年,月和日)创建仅对缺失数据返回null的日期? 我知道,但是我需要首先编写一个字符串,并且运算符和函数都无法轻松处理丢失的数据: 只要是我们最终和: 问题答案: 例如,您可以使用: 不幸的是,Oracle没有一种方法(如果可能)进行转换,否则将返回NULL。SQL Server最近为此目的而引入。 一种选择是为失败的转换编写带有异常处
我的数组包含从0到整数的随机唯一数。最大值。 如何生成唯一的id/签名(int)来唯一地标识每个数组,而不是搜索每个数组并检查每个数字。 例如 每个数组可以有不同的长度,但数字在数组中不重复,可以在其他数组中重复。每个数组的唯一id的目的是通过id来识别它,以便快速进行搜索。数组包含组件的id,数组的唯一签名/id将标识其中包含的组件。 此外,无论数组中的值的顺序如何,生成的id应该是相同的。像{
尝试将我的 Android 应用构建到 APK 时,构建服务器出错且无法构建。但是,在模拟器中,应用程序工作正常,并且我认为错误的类是由编译器创建的(它不是应用程序内部的类之一) 下面是错误日志: -
本文向大家介绍php 根据自增id创建唯一编号类,包括了php 根据自增id创建唯一编号类的使用技巧和注意事项,需要的朋友参考一下 在开发过程中,我们数据表一般都使用自增数字作为id主键,而id是数字型,不容易理解。我们把id按一定格式转为编号后,很容易根据编号知道代表的是什么内容。 例如订单表id=20160111197681234,只看id我们并不知道这个id是订单表的id,而转为编号O-20
本文向大家介绍PHP生成唯一订单号,包括了PHP生成唯一订单号的使用技巧和注意事项,需要的朋友参考一下 在网上找了一番,发现这位同学的想法挺不错的,redtamo,具体的请稳步过去看看,我作简要概述,该方法用上了英文字母、年月日、Unix 时间戳和微秒数、随机数,重复的可能性大大降低,还是很不错的。使用字母很有代表性,一个字母对应一个年份,总共16位,不多也不少. 1. 生成效果: 2. 输出结果
问题内容: 我在sql中有一个用户表,他们每个都有生日。我想将他们的出生日期转换为他们的年龄(仅年),例如日期:年龄:14,并将更改为年龄: 在这里,我想显示用户的日期: 问题答案: 功能:, MySQL > = 5.0.0 功能:,