我需要生成一个唯一的编号,目前的逻辑是:
有一个表
tableA
,其中包括列id
(pk),code
(varchar,unique)和codeValue
(int)。
生成唯一编号时,系统会通过
codeValue
得到code
,
如果没有记录,系统将在该表中插入一条codeValue
为1的记录,并以xxx00001
作为唯一编号;
如果有记录,系统将把codeValue
+1作为唯一编号,如xxx00002
,并将表中的codeValue
更新为2。
java调用如下(srping boot 2.x):
@Service
class AServiceImpl extends BaseImpl {
public String getNewNo(String p, Integer b, String d) {// `code` of `tableA` is composed of p, b and d.
StringBuffer sb = new StringBuffer();
synchronized(getNoType(b, p, d)) {
BeanA beanA = query record from `tableA` by `code`
Integer s = null;
if (beanA.getId() == null) {
s = 1;
beanA.setCodeValue(1);
insert beanA to `tableA`;
} else {
s = 1 + beanA.getCodeValue();// beanA.getCodeValue() return Integer
beanA.setCodeValue(s);
update beanA;
}
sb.append(beanA.getCode());
sb.append(String.format("%04d", s));// 1 -> 0001, 1234 -> 1234
}
return sb.toString();
}
}
AServiceImpl
延伸BaseImpl
并且getNoType
位于BaseImpl
中,如下所示:
@Service
public class BaseImpl {
private static Map<String, Object> noType = new ConcurrentHashMap<>();
public Object getNoType(Integer b, String p, String d) {
StringBuffer sb = new StringBuffer();
if( b != null ) sb.append(b);
if( !StringUtil.isEmptyString(p) ) sb.append(p);
if( !StringUtil.isEmptyString(d) ) sb.append(d);
String type = sb.toString();
noType.putIfAbsent(type, new Object());
return noType.get(type);
}
}
我想要的是,对于相同的代码,即使存在并发条件,
getNewNo
也总是返回不同的唯一数字,所以我尝试使用synchronized
和ConcurrentHashMap
来实现这个目标。
但是,有时当有两个人同时执行操作时,
getNewNo
会返回相同的数字(对于相同的code
),例如有两个相同的数字xxxx0488
。(插入/更新数据库时没有错误)
可能出现哪些问题?谢谢!
您可以引入锁存储并以原子方式从那里获取锁。 尝试这样的事情
@Service
public class AServiceImpl extends BaseImpl {
private final Map<NoType, Lock> lockMap = new ConcurrentHashMap<>();
record NoType(String p, Integer b, String d) {
}
public String getNewNo(String p, Integer b, String d) {
StringBuffer sb = new StringBuffer();
var noType = new NoType(p, b, d);
lockMap.putIfAbsent(noType, new ReentrantLock());
Lock lock = lockMap.get(noType);
lock.lock();
try {
BeanA beanA = query record from `tableA`by `code`
Integer s = null;
if (beanA.getId() == null) {
s = 1;
beanA.setCodeValue(1);
insert beanA to `tableA`;
} else {
s = 1 + beanA.getCodeValue();// beanA.getCodeValue() return Integer
beanA.setCodeValue(s);
update beanA;
}
sb.append(beanA.getCode());
sb.append(String.format("%04d", s));// 1 -> 0001, 1234 -> 1234
return sb.toString();
} finally {
lock.unlock();
}
}