Java生成ID不同场景的实现方式

一:使用数据库自增的方式

这个对于业务不复杂的需求来说,直接设置主键自增的形式,往表里面插入数据时该字段会默认从1开始往上累加。
这种方式不存在连续性,也就是说若表中存有3行数据,ID字段为1,2,3 那么当删除字段2时就变为1,3不具有连续性.合并表会出现ID重复的情况,上面说个使用自增ID能够在单个表中保证ID字段唯一,但两个表何为1个表,时不具有这种性质的。

二:使用全球唯一的UUID()方式

UUID是一种通用唯一识别码,而且本机生成不耗费资源,目的是用于分布式环境中唯一生成标志码,是由32个16进制数组成,
主要包括三部分:
(1)当前日期和时间,UUID的第一个部分是当前日期和时间,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
(2)时钟序列
(3)全局唯一的IEEE机器识别号(如果有网卡,从网卡获得,没有网卡以其他方式获得)
目前很多公司都使用该方式获取唯一标识,但该方式存在浪费不必要的数据库资源,且完全没有可读性,具体实现方式可参考如下

public static String getUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
public synchronized static final String buildID() {
		synchronized (IDGenerate.class) {
			return UUID.randomUUID().toString().replaceAll("-", "");
		}
	}

三:单用户随机短信验证码

在平时登录,修改密码,验证相关信息时,我们经常需要实时接收一个短信验证码,输入验证码校验通过过可进行下一步操作,已确保该操作通过该手机号持有人所操作。
生成短信验证码的方式只要确保单用户随机的即可,生成位数可自定义:

            //生成6位随机数字
            System.out.println((int)((Math.random()*9+1)*100000));
            //生成5位随机数字
            System.out.println((int)((Math.random()*9+1)*10000));
            //生成4位随机数字
            System.out.println((int)((Math.random()*9+1)*1000));
            //生成3位随机数字
            System.out.println((int)((Math.random()*9+1)*100));
            //生成2位随机数字
            System.out.println((int)((Math.random()*9+1)*10));
            //生成1位随机数字
            System.out.println((int)((Math.random()*9+1)));

四:生成有序的唯一ID

UUID()和主键自增的方式无法满足我们业务需求场景时,我们通常会想到自定义唯一的ID,生成规则可通过需求场景内部定义。如通常使用时间戳加随机数的方式,这种方式可读性偏好,效率高,但无法合理的保证唯一性,因为随机数定义的长度,决定了某一时间内出现相同的订单号为随机数长度分之一的概率。
如下方式可生成唯一高可读性的ID,但高并发下可能效率偏慢,但每秒万级还是不在话下的:

    public synchronized String getLocalOrderNo() {
    	//自定义订单号前缀
    	String noPrefix  = "NO";
    	String nowStr = noPrefix + DateUtil.getStringDateMin();
    	String sendNo = (String) redisTemplate.opsForValue().get("OrderNo");
    	if (StringUtils.isEmpty(sendNo)) {
    		sendNo = localSendMapper.getLocalSendSeqNo();
    	}
    	if (StringUtils.isEmpty(sendNo)) {
    		sendNo = noPrefix+DateUtil.getStringDateMin()+"D00000000";
    	}
    	//截取前面部分做对比看是不是同一天的订单序号不是则重新开始计数
    	String subStr = sendNo.substring(0,11);
    	if (subStr.equals(nowStr)) {
    		String prefix = sendNo.substring(0,sendNo.indexOf("D")+1);
    		StringBuilder seqNoBuilder = new StringBuilder(prefix);
    		String excuteOrderNo = sendNo.substring(sendNo.indexOf("D")+1);
    		int no = Integer.parseInt(excuteOrderNo);
    		no +=1;
    		NumberFormat  numberFormat = new DecimalFormat("00000000");
    		String serails = numberFormat.format(no);
    		seqNoBuilder.append(serails);
    		sendNo = seqNoBuilder.toString();
    	} else {
    		StringBuilder seqNoBuilder = new StringBuilder();
    		String date = noPrefix + DateUtil.getStringDateMin();
    		seqNoBuilder.append(date);
    		seqNoBuilder.append("D00000001");
    		sendNo = seqNoBuilder.toString();
    	}
    	redisTemplate.opsForValue().set("OrderNo", sendNo);
    	return  sendNo;
    }

如上这种方式生成的订单可自定义自己所需要的规则,需要在redis中存入对应的值,用来提升查询单号的效率,如果仅需可读性无需完全自定义,可以使用时间戳加UUID()的哈希值,这种方式有一定的可读性,效率也高。


public static String getOrderIdByUUId() {
        Date date=new Date();
        DateFormat format = new SimpleDateFormat("yyyyMMdd");
        String time = format.format(date);
        int hashCodeV = UUID.randomUUID().toString().hashCode();
        if (hashCodeV < 0) {//有可能是负数
            hashCodeV = -hashCodeV;
        }
        // 0 代表前面补充0
        // 4 代表长度为4
        // d 代表参数为正数型
        return time + String.format("%011d", hashCodeV);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章