通用業務編號生成工具類(MyBatis-Plus + Spring Boot)詳解 + 3種調用方式

在企業應用開發中,我們經常需要生成類似 BZ?-240704-0001 這種“業務編號”,它通常具有以下特點:

  • 前綴:代表業務類型,如 BZ?表示包裝

  • 日期:年月日格式,通常為 yyMMdd

  • 序列號:當天內遞增,如 00010002

本文介紹一個支持 自動去重、遞增編號、通用字段提取 的工具類,并提供了三種調用方式,適配不同場景。

📦 工具類源碼:

package com.kakarote.pm.common;import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.IService;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.stereotype.Component;import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;/*** 生成唯一業務編號,格式如 FK-240703-0001,支持重試避免重復*/
@Component
public class DocumentCodeGeneratorUtil {/*** 自動生成唯一編號(支持重試,防止重復)* @param prefix        編號前綴,如 "FK"* @param service       MyBatis-Plus 的 Service 對象,用于執行數據庫查詢* @param columnGetter  要生成編號的字段引用,如 Entity::getDocumentCode* @param <T>           實體類* @return              唯一編號,例如 FK-240704-0001*/private static final int MAX_RETRY = 5;public <T> String generateUniqueCode(String prefix, IService<T> service, SFunction<T, String> columnGetter) {int retry = 0;while (retry < MAX_RETRY) {String code = generateCodeByDatePrefix(prefix, service, columnGetter);int count = service.lambdaQuery().eq(columnGetter, code).count();if (count == 0) {return code;}retry++;}throw new RuntimeException("編號生成失敗:連續5次生成重復編號,請稍后重試!");}private <T> String generateCodeByDatePrefix(String prefix, IService<T> service, SFunction<T, String> columnGetter) {String currentDate = new SimpleDateFormat("yyMMdd").format(new Date());String prefixWithDate = prefix + "-" + currentDate + "-";List<T> list = service.lambdaQuery().likeRight(columnGetter, prefixWithDate).orderByDesc(columnGetter).last("limit 1").list();int nextSeq = 1;if (!list.isEmpty()) {try {T entity = list.get(0);String fieldName = getFieldName(columnGetter);String maxCode = (String) PropertyUtils.getProperty(entity, fieldName);String[] parts = maxCode.split("-");if (parts.length == 3) {nextSeq = Integer.parseInt(parts[2]) + 1;}} catch (Exception e) {throw new RuntimeException("反射獲取字段值失敗", e);}}return prefixWithDate + String.format("%04d", nextSeq);}/*** 通過 SerializedLambda 獲取字段名, 例:User::getName => name*/private <T> String getFieldName(SFunction<T, ?> fn) throws Exception {Method writeReplace = fn.getClass().getDeclaredMethod("writeReplace");writeReplace.setAccessible(true);SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(fn);String implMethodName = serializedLambda.getImplMethodName();if (implMethodName.startsWith("get")) {return Introspector.decapitalize(implMethodName.substring(3));} else if (implMethodName.startsWith("is")) {return Introspector.decapitalize(implMethodName.substring(2));}return implMethodName;}
}

?使用方式(3種場景)?

場景 1:在當前 ServiceImpl 內部調用(推薦)

@Autowired

private DocumentCodeGeneratorUtil documentCodeGeneratorUtil;

@Override

public void savePack() { ? ?

String code = documentCodeGeneratorUtil.generateUniqueCode("BZ", this, PmPack::getDocumentCode); ? ?

pmPack.setDocumentCode(code); ? ?

save(pmPack);

}

說明:this 是當前類,已繼承 BaseServiceImpl,本身就是 IService<PmPack>
例如:

如果沒有就采用方式二的本身的service調用就行

場景 2:在其他類(如 Controller)中調用

@Autowired
private PmPackService pmPackService;

@Autowired
private DocumentCodeGeneratorUtil documentCodeGeneratorUtil;

public void createPackFromController() {
String code = documentCodeGeneratorUtil.generateUniqueCode("BZ", pmPackService, PmPack::getDocumentCode);
}

場景 3:使用 Spring 上下文動態獲取(非推薦,僅限無法注入時)

PmPackService service = SpringContextHolder.getBean(PmPackService.class);

String code = documentCodeGeneratorUtil.generateUniqueCode("BZ", service, PmPack::getDocumentCode);

?你需要實現 SpringContextHolder

@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext context;

? ? @Override
public void setApplicationContext(ApplicationContext ctx) {
context = ctx;
}

? ? public static <T> T getBean(Class<T> clazz) {
return context.getBean(clazz);
}
}

🔧 所需依賴(pom.xml)

// MyBatis Plus 核心依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>

//?BeanUtils(反射讀取字段值)

<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>?

示例輸出

假設今天是 2024年7月4日,編號前綴為 FK,數據庫已有最大編號為:

BZ-240704-0001? 以此類推第二天重置
如果重復,會自動重試最多 5 次。

例如:

優點總結

功能支持情況
日期前綴?
自增長序列?
多表復用?
自動重試去重?
支持 Lambda 字段提取?
支持多個業務類型前綴?

結語:

?這個工具類已經在多個模塊(如:付款利息、包裝、檢驗)中實際應用,穩定可靠,適配 MyBatis-Plus 體系,簡潔靈活。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/88025.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/88025.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/88025.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前端相關性能優化筆記

1.打開速度怎么變快 - 首屏加載優化2.再次打開速度怎么變快 - 緩存優化了3.操作怎么才順滑 - 渲染優化4.動畫怎么保證流暢 - 長任務拆分2.1 首屏加載指標細化:1.FP(First Paint 首次繪制) 2.FCP(First contentful Paint 首次內容繪制)&#xff0c;FP 到 FCP 中間其實主要是 SPA…

7.7晚自習作業

實操作業02&#xff1a;Spark核心開發 作業說明 請嚴格按照步驟操作&#xff0c;并將最終結果文件&#xff08;命名為&#xff1a;sparkcore_result.txt&#xff09;于20點前上傳。結果文件需包含每一步的關鍵命令執行結果文本輸出。 一、數據讀取與轉換操作 上傳賬戶數據$…

手機FunASR識別SIM卡通話占用內存和運行性能分析

手機FunASR識別SIM卡通話占用內存和運行性能分析 --本地AI電話機器人 上一篇&#xff1a;手機無網離線使用FunASR識別SIM卡語音通話內容 下一篇&#xff1a;手機通話語音離線ASR識別商用和優化方向 一、前言 書接上一文《阿里FunASR本地斷網離線識別模型簡析》&#xff0c;…

虛幻引擎Unreal Engine5恐怖游戲設計制作教程,從入門到精通從零開始完整項目開發實戰詳細講解中英字幕

和大家分享一個以前收集的UE5虛幻引擎恐怖游戲開發教程&#xff0c;這是國外一個大神制作的視頻教程&#xff0c;教程從零開始到制作出一款完整的游戲。內容講解全面&#xff0c;如藍圖基礎知識講解、角色控制、高級交互系統、高級庫存系統、物品檢查、恐怖環境氛圍設計、過場動…

多人協同開發時Git使用命令

拉取倉庫代碼 # 拉取遠程倉庫至本地tar_dir路徑 git clone gitgithub.com:your-repo.git target_dir # 默認是拉取遠程master分支&#xff0c;下面拉取并切換到自己需要開發的分支上 # 假設自己需要開發的分支是/feature/my_branch分支 git checkout -b feature/my_branch orig…

線性表——雙向鏈表

線性表——雙向鏈表1. 雙向鏈表的實現1.1 簡單圖例1.2 結點的定義1.3 新結點的創建1.4 鏈表的初始化1.5 結點的插入1.5.1 頭部插入&#xff08;頭插&#xff09;1.5.2 尾部插入&#xff08;尾插&#xff09;1.5.3 任意位置&#xff08;前&#xff09;插入1.6 結點的刪除1.6.1 頭…

Java后端技術博客匯總文檔

文章目錄 前言Java后端匯總鏈接Java基礎知識點數據結構算法&#xff08;Java實現&#xff09;算法知識點合集算法刷題算法競賽AcWing課程藍橋杯AB組輔導課合集&#xff08;更新中…&#xff09; 源碼分析redission 數據庫SQL ServerMySQLRedis -Canal JUC并發編程JVMNetty日志框…

QT 菜單欄設計使用方法

目錄 常用設置函數 多個QAction的單選設置 ???????菜單相關類 ??????? 系統菜單的生成和響應 使用代碼添加系統菜單 使用UI設計器設計系統菜單 使用Qt設計及界面時&#xff0c;常用的兩種方式添加菜單&#xff0c;第一使用UI界面添加&#xff0c;第二種 在…

AIGC領域AI藝術,打造個性化藝術作品

AIGC領域AI藝術,打造個性化藝術作品 關鍵詞:AIGC、AI藝術、生成對抗網絡、個性化創作、深度學習、藝術風格遷移、創意計算 摘要:本文深入探討了AIGC(人工智能生成內容)在藝術創作領域的應用,重點分析了如何利用AI技術打造個性化藝術作品。文章從技術原理出發,詳細解析了生…

基于Flask+Jinja2的快捷教務系統(后端鏈接到新版正方教務系統)

快捷教務系統&#xff08;Easy Educational Administration Management System, EasyEAMS&#xff09; 項目簡介 EasyEAMS 是一個基于 Flask Jinja2 的現代化教務系統 Web 應用。學生可通過網頁端登錄&#xff0c;在線查詢個人信息、成績、課表、學業生涯、通知、選課等。系…

EDM自動化與出海獨立開發實用教程

隨著互聯網全球化發展&#xff0c;越來越多的獨立開發者&#xff08;Indie Developer&#xff09;選擇將自己的產品推向海外市場。如何高效地獲客、激活用戶、提升轉化率&#xff0c;成為出海過程中必須解決的問題。EDM&#xff08;電子郵件營銷&#xff09;自動化&#xff0c;…

「日拱一碼」017 深度學習常用庫——TensorFlow

目錄 基礎操作 張量操作&#xff1a; tf.constant 用于創建常量張量 tf.Variable 用于創建可訓練的變量張量 tf.reshape 可改變張量的形狀 tf.concat 可將多個張量沿指定維度拼接 tf.split 則可將張量沿指定維度分割 數學運算&#xff1a; tf.add 張量的加運算 tf.su…

ARM DStream仿真器腳本常用命令

以下是ARM DStream仿真器腳本中常用的命令及其功能分類&#xff0c;結合調試流程和典型應用場景整理&#xff1a; ?? 一、連接與初始化命令 connect 建立與目標設備的連接&#xff0c;需指定接口類型&#xff08;如JTAG/SWD&#xff09;和處理器核心。 示例&#xff1a;conne…

vscode 調試unity

lanch.json { “version”: “0.2.0”, “configurations”: [ { “name”: “Attach to Unity”, “type”: “vstuc”, “request”: “attach” } ] }

金融IT入門知識點

銀行金融IT核心知識點全解析&#xff1a;架構、技術與實踐 一、金融IT的戰略地位與行業特性 金融IT作為銀行業務的核心支撐體系&#xff0c;其發展水平直接決定了銀行服務的效率、安全性與創新能力。截至 2025年&#xff0c;中國銀行業線上化業務占比已達97%&#xff0c;手機銀…

C++——手撕智能指針、單例模式、線程池、String

智能指針今天我們來學習一下C中的智能指針&#xff0c;如果有人不知道C中的智能指針的概念的話&#xff1a;C智能指針是一種基于RAII&#xff08;Resource Acquisition Is Initialization&#xff0c;資源獲取即初始化&#xff09;機制的高級內存管理工具&#xff0c;用于自動化…

Mybatis----留言板

基礎項目&#xff1a;留言板 截止到目前為止&#xff0c;我們已經學習了 Spring&#xff08;只學習了DI&#xff09;、Spring MVC、SpringBoot、Mybatis 這些知識了&#xff0c;已經滿足了做簡單項目的基本要求了&#xff0c;所以接下來我們就從0到1實現表白墻項目。 需求分析…

Web-API-day3 DOM事件進階

一、 事件流 1.事件冒泡 const fa document.querySelector(.father)const son document.querySelector(.son)document.addEventListener(click, function () {alert(我是爺爺)})fa.addEventListener(click, function () {alert(我是爸爸)})son.addEventListener(click, fun…

小波增強型KAN網絡 + SHAP可解釋性分析(Pytorch實現)

效果一覽一、傳統KAN網絡的痛點與突破 1. 傳統KAN的局限性 傳統Kolmogorov-Arnold網絡&#xff08;KAN&#xff09;雖在理論上有可靠的多變量函數逼近能力&#xff0c;但存在顯著瓶頸&#xff1a; 計算效率低&#xff1a;訓練速度慢于MLP&#xff0c;資源消耗大&#xff0c;尤其…

tomcat部署多個端口以及制定路徑部署-vue3

vue3項目tomcat部署記錄 使用hash路由 字符串拼接的圖片地址可以使用import.meta.env.BASE_URL 默認8080 如果部署地址為8080/xc 則設置 vite.config.js中設置base為’/xc/’ outDir設置為xc 打包產物直接拖到webapps目錄下 如果另開一個端口 如8081 設置根目錄訪問 conf/ser…