`
walnut
  • 浏览: 105145 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

通用编号生成器

    博客分类:
  • java
阅读更多
    在编写基于数据库的应用时,有一个常见的需求:某一张表有个编码字段,需要按照一定的规则生成,例如:某订单编号的生成规则是:部门编号+yyyyMMdd+四位流水号,中间部分代表当前的年月日。难点就是如何生成流水号,并且能够保证在多用户并发的情况下,保证流水号不重复。
    得到流水号的方法比较简单:select max(theColumn) from theTable where theColumn like “BBXXXXXXX%”,即在该表中查询具有相同前缀(编码流水号之前的部分)的编码最大值,然后再将流水号部分+1就可以得到新的编码了。为了保证流水号不重复,我们需要锁定数据,但是如果锁定该表的话,开销太大,针对该表的增、删、改操作都不能进行。这里采用一个小技巧:我们单独建立一张新的表格,SQL语句如下:
create table LOCK_TABLE (
   TABLE_NAME  VARCHAR(20) not null
   constraint PK_ LOCK_TABLE  primary key  (TABLE_NAME)
)
这个表只有一个字段,数据即需要我们生成编号的表名。我们在计算某个表的当前最大流水号之前,首先锁定LOCK_TABLE表中数据为该表名的那条数据(具体方法后文有介绍),然后再执行上面的select max(**) …… 操作,得到新的编码。请注意:上面的锁定LOCK_TABLE表中一行数据;查询最大编码是在一个事务中完成的。
    锁定LOCK_TABLE表中数据为该表名的那条数据的方法:就是在普通的SQL语句中加入锁定的关键字,对于Oracle来说是: for update ;对于SQLServer来说是 with (holdlock)。

    具体实现代码如下:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.log4j.Logger;

public class KeyGenerator{

	private Logger logger = Logger.getLogger(KeyGenerator.class);
	private Connection conn;
	private String tableName;
	private String columnName;
	private String head;
	private int numCount;
	private String lockString;

	/**
	 * @param tableName - 表名
	 * @param columnName - code 对应 字段名
	 * @param head - 缺少最后流水号的 code 前端
	 * @param numCount - 最后流水号数字的个数
	 * @param lockString - 对应于 LOCK_TABLE表中的字符串名
	 */
	
	// example used in xxxCreateBO
	// KeyGenerator keyGenerator= new KeyGenerator(getSession().connection(),"TS_GXCPRD","APP_ID","2005210204A",4,"TS_GXCPRD");
	// String key=keyGenerator.getKey();
	
	
	public KeyGenerator(Connection conn,String tableName,String columnName,String head,int numCount,String lockString) {
		this.conn=conn;
		this.tableName=tableName;
		this.columnName=columnName;
		this.head=head;
		this.numCount=numCount;
		this.lockString=lockString;
		
	}
	
	private String computeNewCode(String maxCode,String head,int numCount){
		
		String newCode="";
		if(maxCode!=null){
			int i=head.length();
			int j=maxCode.length();
			int k=j-i;
			
			String numPart=maxCode.substring(i,j);
			int theInt= new Integer(numPart).intValue();
			theInt++;
			String numString =new Integer(theInt).toString();
			k=k-numString.length();
			String temp0="";
			for(;k>0;k--){
				temp0=temp0+"0";
			}
			numString=temp0+numString;
			newCode=head+numString;
		}
		else{
			String temp0="";
			for(int k=numCount-1;k>0;k--){
				temp0=temp0+"0";
			}
			newCode=head+temp0+"1";
		}
		return newCode;
	}


	public String getKey() {

		String oracleLockStr=" for update ";
		String sqlServerLockStr=" with (holdlock) ";
		
		String sql1 = " SELECT * FROM " + "LOCK_TABLE ";
		// 用来锁定表中记录
		// 如果是SQLServer数据库用 with (holdlock),放在where条件前面
		// SQLServer 例子:select * from LOCK_TABLE with (holdlock) where TABLE_NAME like 'aaa%';
		// 如果是oracle数据库用 for update,放在where条件后面
		// Oracle 例子:   select * from LOCK_TABLE where TABLE_NAME like 'aaa%' for update;
		sql1 = sql1+sqlServerLockStr;
		sql1 = sql1+" WHERE " + "TABLE_NAME" + " LIKE '" + lockString.trim() + "'";
		
		String sql2 = " SELECT MAX(" + columnName+ ") AS A FROM "+ tableName ;
		sql2 = sql2+" WHERE " + columnName + " LIKE '" + head.trim() + "%' ";
		
		PreparedStatement pstm1 = null;
		PreparedStatement pstm2 = null;
		Statement stmt = null;
		ResultSet rset1 = null;
		String maxCode="";
		String newCode="";
		
		try {
			pstm1 = conn.prepareStatement(sql1);
			pstm1.executeQuery();
			pstm2 = conn.prepareStatement(sql2);
			rset1 = pstm2.executeQuery();
			rset1.next();
			maxCode=rset1.getString("A");
			
			newCode=computeNewCode(maxCode,head,numCount);
			
			logger.info("newCode:"+newCode);
			System.out.println("newCode:"+newCode);
			return newCode;

		}  catch (Exception e) {
			System.out.println(e);
			e.printStackTrace();
			return null;

		}
		finally{
			try {
				if (rset1 != null)
					rset1.close();
			} catch (SQLException e1) {
			}

			try {
				if (pstm1 != null)
					pstm1.close();
				if (pstm2 != null)
					pstm2.close();
				if (stmt != null)
					stmt.close();
			} catch (SQLException e1) {
			}
		}

	}

}



分享到:
评论
1 楼 xiaoluojinsheng 2009-01-13  
好文章,我先试试!!

相关推荐

    IMEI批量生成器

    IMEI批量生成器是一款工具软件,主要用于快速生成大量IMEI(国际移动设备识别码)号码。IMEI是每个移动通信设备如手机、平板电脑等独一无二的标识符,由15位数字组成,用于区分全球范围内的移动设备。下面将详细阐述...

    彩票生成器

    《C++实现彩票生成器详解》 在编程领域,C++是一种强大且广泛应用的编程语言,尤其在系统软件、游戏开发、嵌入式系统等领域有着广泛的应用。本篇将深入探讨一个C++编写的彩票生成器项目,它是在DOS环境下运行的,...

    订单号随机生成器

    订单号随机生成器是一种软件工具,它主要用于生成唯一的、随机的订单编号,这对于团购商家或者电商平台来说至关重要。在处理大量订单时,一个清晰且独特的订单号可以帮助商家有效地追踪和管理交易,避免混淆或遗漏。...

    最新二维码软件生成器

    二维码软件生成器是一种工具,它能够将各种类型的数据,如文本、网址、联系信息等,编码成二维条形码——即我们熟知的二维码。在现代生活中,二维码的应用日益广泛,从商业广告到个人资料分享,从支付接口到产品追溯...

    条形码生成器源程序

    条形码生成器是一种软件工具,它允许用户创建和打印各种类型的条形码,以便用于产品标识、库存管理、物流追踪等应用场景。在本案例中,我们关注的是一个使用C++编程语言编写的条形码生成器源程序。C++是一种强类型、...

    带自定义数据导出的通用双色球中奖号码更新器

    双色球通用号码更新器 注意事项: 1.绿色版无需安装。 2.运行前请修改app.config文件的数据库路径。路径中不要包含中文!!! 3.双击“LotteryCaptureTool.exe”运行程序。 详细:...

    自动生成编号,Delphi+数据库..rar

    6. **自定义编号生成器** 如果标准的自动编号机制不能满足特定需求,开发者可以编写自定义函数或存储过程来生成编号。例如,可以创建一个包含序列号、日期或字母前缀的复杂编号系统。 7. **数据库设计** 设计...

    EAN13标准条形码生成器

    EAN13(欧洲物品编号13位码)是一种全球通用的商品条形码系统,由国际物品编码协会(GS1)制定,是EAN(欧洲物品编号)系统的组成部分。它由13位数字组成,分为前缀码、厂商识别码、商品项目代码和校验码四个部分: ...

    根据时间自动生成编号,很实用.zip

    public class编号生成器 { public static String 时间戳编号() { long currentTimeMillis = System.currentTimeMillis(); return String.valueOf(currentTimeMillis); } public static String UUID编号() { ...

    手机通讯录VCF文件生成器-易语言

    《手机通讯录VCF文件生成器-易语言详解》 在现代生活中,手机通讯录的重要性不言而喻,它存储着我们与外界联系的关键信息。然而,当我们需要将大量通讯录信息从Excel表格转移到手机时,手动操作既耗时又容易出错。...

    条形码生成工具

    条形码技术主要基于国际标准,如EAN(欧洲物品编号)和UPC(通用产品代码),以及更先进的二维条码如QR码。这些标准确保了全球范围内的互操作性,使得任何支持条形码阅读器的设备都能读取和解码信息。条形码通常包含...

    一维二维通用条码扫描器.zip

    一维二维通用条码扫描器是本站发布的第一个条码扫描项目,条码扫描器在您的手机上使用摄像头读取条形码,查询产品的信息,如价格和评论。此外,还可以读取QR吗和Data Matrix二维条码。传统的条形码,如产品包装上...

    离线生成EAN-13条形码代码(69固定开头,Ver.1)

    EAN-13条形码是一种国际通用的商品条形码编码标准,全称为欧洲物品编号,主要用于标识零售商品、非零售商品以及物流单元。在EAN-13条形码中,数字通常由13位组成,分为前缀码、制造商代码、产品代码和校验码四个部分...

    NXP PCA85133 80×4位RAM低复用率的通用LCD驱动器数据手册.pdf

    内部具有LCD偏置生成器,并且拥有电压跟随缓冲器。它可以驱动80个段,支持多达40个7段字母数字字符,21个14段字母数字字符,以及最多320个任意图形元素。 PCA85133拥有80×4位RAM用于显示数据存储,支持自动增量式...

    NXP PCA85162_DS低复用率的通用LCD驱动器数据手册(英文).pdf

    4. 内置LCD偏置生成器,配备电压跟随缓冲器。 5. 可驱动32个段,支持最多16个7段字母数字字符,最多8个14段字母数字字符,或任何高达128个元素的图形。 6. 拥有32×4位RAM用于存储显示数据。 7. 支持设备子地址边界...

    CIGI介绍文件,教程

    CIGI是一种开放的接口规范,旨在提供一个通用的接口标准,用于连接图像生成器与模拟器。该规范由The Boeing Company维护和更新。CIGI的主要目标是提供一个统一的接口标准,以便于图像生成器和模拟器之间的通信和交互...

    通用权限管理系统

    在数据库设计方面,本系统包含了序列产生器表(Base_Sequence),用于生成各种序列号,以保证数据的唯一性。序列产生器表中包含了序列号前缀、序列号分隔符、升序序列、倒序序列以及步骤描述等字段。序列产生器的创建...

    测试多种方法生成唯一性随机码Demo

    利用CPU ID、MAC地址等硬件信息,结合随机数生成器,可以创建与特定设备关联的唯一编码。但这可能引发隐私问题,不适用于所有场景。 在测试这些方法时,需要关注以下指标: - **唯一性**:确保在所有测试条件下,...

    通用抽奖程序

    2. **公平随机性**:通过高质量的随机数生成器,保证每个参与者的中奖概率一致,避免结果的偏颇。 3. **实时显示**:程序应实时展示抽奖过程,如滚动名单、中奖结果等,增加活动的紧张感和观赏性。 4. **结果记录*...

    DotNetClasses:.NET和.NET Core生成器的GeneXus标准类

    .NET和.NET Core生成器的GeneXus标准类。 建造状态 科 状态 主 贝塔 模组 名称 描述 包裹编号 Gx加密 与.NET和.NET Core通用的与基于Twofish算法的加密相关的类 GeneXus加密 GxEncryptCMD 允许对数据进行加密和...

Global site tag (gtag.js) - Google Analytics