方法一:
(一).订单号、流水号要求:
- 单数据库下支持高并发
- 唯一的新单号,不保证连续性
- 不保存数据库内唯一,保证表内唯一
(二).设计思路
- 如何保证数据唯一?
先看下单号样例:A04190701000001 。
A04:组织代码,190701:当前年月日,000001:可变长度流水号。
通过组织代码+当前年月日+定长流水号,保证单表内唯一。
2.如何保证单表为唯一?
数据库中记录的形式。数据库中将存在流水生成记录表,记录如下字段:
name(PO对象class名_组织代码_当前年月日),value(当前生成次数)。
生成sql存储过程,获取流水序号。
(三).代码实现
/**
* 生成序列号的工具。 <br>
* 可支持单数据库的并发操作。
*/
public class SequenceGenerator {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
private static final int DEFAULT_FLOW_NO_LEN = 6;
/**
* 生成定长序列号。
*
* @param name
* 名称
* @param len
* 长度
* @return
* @throws ServiceException
*/
public static String nextValue(String name, int len) throws ServiceException {
Assert.assertArgumentNotNull(name, "name");
int value = nextValue(name);
return String.format("%0" + len + "d", value);
}
/**
* 生成序列号。
*
* @param name
* 序列名称。
* @return
* @throws ServiceException
*/
public static int nextValue(String name) throws ServiceException {
Assert.assertArgumentNotEmpty(name, "name");
String sql = "select _nextval(?) v";
ICommonDao commonDao = ApplicationContextUtil.getBean(ICommonDao.class);
Map<String, Object> results = commonDao.findOneForJdbc(sql, name);
if (results.size() <= 0) {
throw new ServiceException("生成序列号失败");
}
Object value = results.get("v");
if (value == null) {
throw new ServiceException("生成序列号失败");
}
return (Integer) value;
}
/**
* 生成单号。 <br>
* 默认6位流水号
*
* @param billClass
* 单据对象类。
* @return 唯一的新单号,但不保证连续性。
* @throws ServiceException
*/
public static String nextBillNumber(Class<?> billClass) throws ServiceException {
return nextBillNumber(billClass.getSimpleName(), DEFAULT_FLOW_NO_LEN);
}
/**
* 生成单号。 <br>
* 默认6位流水号
*
* @param billType
* 单据类型,一般取实体短名称,禁止为null。
* @return 唯一的新单号,但不保证连续性。
* @throws ServiceException
*/
public static String nextBillNumber(String billType) throws ServiceException {
return nextBillNumber(billType, DEFAULT_FLOW_NO_LEN);
}
/**
* 生成单号。
*
* @param billType
* 单据类型,一般取实体短名称,禁止为null。
* @param flowNumLenth
* 流水号长度。-1表示不固定长度。
* @return 唯一的新单号,但不保证连续性。
* @throws ServiceException
*/
public static String nextBillNumber(String billType, int flowNumLenth) throws ServiceException {
final String orgCode = "A04";
return nextBillNumber(billType, orgCode, flowNumLenth);
}
/**
* 生成单号。 <br>
* 生成规格: 5位组织代码 + 6位日期(yyMMdd) + flowNumLenth位流水号。 其中,流水号每日重复计算。 <br>
* 示例: 00001 161206 00001
*
* @param billType
* 单据类型,一般取实体短名称,禁止为null。
* @param orgCode
* 当前组织代码,可空。
* @param flowNumLenth
* 流水号长度。-1表示不固定长度。
* @return 唯一的新单号,但不保证连续性。
* @throws ServiceException
*/
public static String nextBillNumber(String billType, String orgCode, int flowNumLenth)
throws ServiceException {
Assert.assertArgumentNotEmpty(billType, "billType");
String suffix = StringUtils.trimToEmpty(orgCode);
if (StringUtils.isNotBlank(suffix)) {
suffix += "_";
}
final String dateStr = sdf.format(new Date());
suffix += dateStr;
// 取流水号
final String key = billType + "_" + suffix;
final int value = nextValue(key);
String test = String.format("%s%" + (flowNumLenth < 0 ? "" : "0" + flowNumLenth) + "d",
orgCode + dateStr, value);
return test;
}
/**
* 取当前组织代码
*
* @return
* @throws ServiceException
*/
private static String getCurrentOrgCode() throws ServiceException {
}
}
-- sql存储过程
begin
declare bExists int;
select count(1) into bExists from wm_sequence where `_name` = in_name;
if bExists = 0 then
INSERT INTO `wm_sequence`(`_name`, `_value`) VALUES (in_name, 0);
end if;
update wm_sequence SET _value = LAST_INSERT_ID(_value+1) where _name = in_name;
return (select LAST_INSERT_ID());
end