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);

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