数据库中有自增长的字段,但是自增长字段有他的局限性,有的数据库是不支持自增长的。在开发过程中,部分客户业务需要生成业务的流程号,单纯的数字无法满足需求,于是就产生了编写一个序列号生成器的想法。
1、首先创建数据库表
create table sys_max_number (
mn_id varchar(32) not null,
mn_key_name varchar(64) not null,
mn_key_value bigint not null default 0,
mn_remark varchar(512) not null,
constraint PK_SYS_MAX_NUMBER primary key (mn_id),
constraint AK_UNIQUE_MAX_NUMBER_SYS_MAX_ unique (mn_key_name)
)
go
各个字段的含义如下:数据库主键,流水号的key,键值(默认0),流水号key说明
代码如下:
package com.sys.maxnumber;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.work.core.db.DbConnectionFactory;
/**
* @author wangmingjie
* @date 2009-8-4上午09:59:15
*/
public class KeyInfo {
private static Log log = LogFactory.getLog(KeyInfo.class);
private long keyMax; // 最大键值
private long keyMin; // 最小键值
private long nextKey; // 下一个键值
private int poolSize; // 缓存大小,注意,太大容易浪费号码
private String keyName; // 主键,用在哪个应用中的。
public KeyInfo(int poolSize, String keyName) {
this.poolSize = poolSize;
this.keyName = keyName;
retrieveFromDB(5); // 注意不能直接使用这个类,否则在多线程的情况下,会出问题。
//上面必须初始化的时候调用,否则出错!目的是为了首先要给nextKey赋值
}
public long getKeyMax() {
return keyMax;
}
public long getKeyMin() {
return keyMin;
}
/**
* 获取下一个字符串的键值。如果不符合长度,前面用零补齐;<br>
* 例如参数是四,intkey=10,那么返回0010
* 注意,这里面调用的是getNextIntKey方法。
* 2,147,483,647 ,
* @param paddingLength
* 字符串长度,最大长度为10位。因为整形的缘故。
*
* @return 如果参数长度大于指定的长度了,那么返回实际长度的字符串。
*/
public String getNextStringKey(final int paddingLength) {
String s = Long.toString(getNextIntKey());
int len = s.length();
if (len < paddingLength) {
StringBuffer buf = new StringBuffer(paddingLength);
for (int i = 0; i < paddingLength - len; i++) {
buf.append('0');
}
buf.append(s);
s = buf.toString();
}
return s;
}
/**
* 获取下一个整型的键值;
*
* @return
*/
public synchronized int getNextIntKey() {
if (nextKey > keyMax) {
retrieveFromDB(5);
}
return (int)(nextKey++);
}
/**
* 获取长整形的键值
* @return
*/
public synchronized long getNextLongKey(){
if (nextKey > keyMax) {
retrieveFromDB(5);
}
return nextKey++;
}
private void retrieveFromDB(int count) {
if (count == 0) {
if(log.isErrorEnabled())
log.error("最终获取序列号失败!放弃继续获取...");
return;
}
boolean success = false;
long keyFromDB = 0;
String updateSql = "UPDATE sys_max_number SET mn_key_value = mn_key_value + "
+ poolSize + " WHERE mn_key_name =? ";
String selectSql = "SELECT mn_key_value FROM sys_max_number WHERE mn_key_name = ? ";
Connection conn = null;
PreparedStatement pstUpdate = null;
PreparedStatement pstSelect = null;
ResultSet rst = null;
try {
//conn = DbConnectionFactory.getJdbcConn();
conn = DbConnectionFactory.getConnection();//从连接池中获取数据库连接
conn.setAutoCommit(false);
pstUpdate = conn.prepareStatement(updateSql);
pstUpdate.setString(1, keyName.trim());
pstUpdate.executeUpdate();
pstSelect = conn.prepareStatement(selectSql);
pstSelect.setString(1, keyName.trim());
rst = pstSelect.executeQuery();
while (rst.next()) {
keyFromDB = rst.getLong(1);
}
conn.commit();
success = true;
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
if(log.isWarnEnabled())
log.warn("获取序列号失败!", e);
} finally {
try {
if (rst != null)
rst.close();
} catch (SQLException e) {
}
try {
if (pstUpdate != null)
pstUpdate.close();
} catch (SQLException e) {
}
try {
if (pstSelect != null)
pstSelect.close();
} catch (SQLException e) {
}
try {
if (conn != null)
conn.close();
} catch (SQLException e) {
}
}
keyMax = keyFromDB;
keyMin = keyFromDB - poolSize + 1;
nextKey = keyMin;
if (!success) {
if(log.isWarnEnabled())
log.warn("警告,因线程争夺,获取下一个序列号失败。进行下一次尝试...");
// Call this method again, but sleep briefly to try to avoid thread contention.
try {
Thread.sleep(75);
} catch (InterruptedException ie) {
}
retrieveFromDB(count - 1);
}
}
}
======================================
package com.sys.maxnumber;
import java.util.HashMap;
//
//import org.apache.commons.logging.Log;
//import org.apache.commons.logging.LogFactory;
/**
* @author wangmingjie
* @date 2009-8-4上午10:33:19
*/
public class KeyGenerator {
//private static Log log = LogFactory.getLog(KeyGenerator.class);
private static HashMap<String, KeyGenerator> kengens = new HashMap<String, KeyGenerator>(
10);
/* 最小为1,因为数据库中,默认的是0,生成的序列值最小为1 */
private static final int POOL_SIZE = 1;
private KeyInfo keyinfo;
/**
* 构造方法。
* @param poolSize 每次获取几个数值,放到缓存中。如果小于1,那么默认为1。<br>
* 建议值为2,这样每次重新启动应用最多浪费一个数,而且能够提高到两倍的效率。<br>
* 如果使用1,废号的几率是最低的。但是不能应用在高并发的系统中。
* @param keyName 对应最大好表的键。
*/
private KeyGenerator(int poolSize,String keyName) {
if(poolSize<1){
poolSize=POOL_SIZE;
}
keyinfo = new KeyInfo(poolSize, keyName);
}
/**
* 保证线程安全。
* @param poolSize 缓存大小
* @param keyName 主键名称
* @return
*/
public static synchronized KeyGenerator getInstance(int poolSize,String keyName) {
KeyGenerator keygen;
if (kengens.containsKey(keyName)) {
keygen = kengens.get(keyName);
//System.out.println("从缓存中获得"+keyName);
} else {
keygen = new KeyGenerator(poolSize,keyName);
kengens.put(keyName, keygen); //注册到hashmap中。提高效率
//System.out.println("注册了"+keyName);
}
return keygen;
}
/**
* 获取到int类型的序列值
* @return
*/
public int getNextIntKey() {
return keyinfo.getNextIntKey();
}
/**
* 获取到long类型的序列值。一般不会用到。除非是像移动联通,这样的,每天的通话记录都是上亿的。
* @return
*/
public long getNextLongKey() {
return keyinfo.getNextLongKey();
}
/**
* 获取到string类型的序列值
* @param paddingLength 返回string的长度,最大长度是10位。如果使用long可以到19位。
* @return
*/
public String getNextStringKey(int paddingLength){
return keyinfo.getNextStringKey(paddingLength);
}
}
================测试类如下==========================
package com.sys.maxnumber;
/**
* 注意测试的时候,修改KeyInfo的retrieveFromDB方法,使其通过jdbc连接。
* 使用的时候注意,keyname必须在最大号表中存在!
* @author wangmingjie
* @date 2009-8-4上午10:38:19
*/
public class KeyClient {
//一般使用方法,KeyGenerator.getInstance(1,"BugProject").getNextIntKey();
//在并发数量多的情况下面,提高poolsize的值,这样就可以减少数据库的访问次数。
/**
* 使用方法
*
* @param args
*/
public static void main(String[] args) {
//KeyGenerator keygen = KeyGenerator.getInstance(1,"BugProject");
//
//for (int i = 0; i < 25; i++) {
//System.out.println("key(" + (i + 1) + ")= " + keygen.getNextIntKey());
//}
//
//for (int i = 0; i < 25; i++) {
//System.out.println("key(" + (i + 1) + ")= " + keygen.getNextStringKey(6));
//}
ThreadA t = new ThreadA();
Thread t1 = new Thread(t, "A");
Thread t2 = new Thread(t, "B");
Thread t3 = new Thread(t, "C");
Thread t4 = new Thread(t, "D");
t1.start();
t2.start();
t3.start();
t4.start();
//访问多了就出现事务死锁。所以高并发系统中必须提高缓存数量。
//java.sql.SQLException: 事务(进程 ID 143)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
//Bkey A(12)= 000137
//ThreadA ta = new ThreadA();
//ta.run();
//ThreadB tb = new ThreadB();
//tb.run();
}
}
class ThreadA implements Runnable{
public void run(){
KeyGenerator keygen = KeyGenerator.getInstance(1,"BugProject");
for (int i = 0; i < 500; i++) {
System.out.println(Thread.currentThread().getName()+"key A(" + (i + 1) + ")= "
+ keygen.getNextIntKey()+"||"
+ keygen.getNextIntKey()
+"||"
+ keygen.getNextStringKey(4));
}
}
}
class ThreadB implements Runnable{
public void run(){
KeyGenerator keygen = KeyGenerator.getInstance(1,"BugProject");
for (int i = 0; i < 500; i++) {
System.out.println(Thread.currentThread().getName()+"key B(" + (i + 1) + ")= " + keygen.getNextStringKey(6));
}
}
}
==============pojo================
package com.sys.model;
import java.io.Serializable;
/**
*
*/
public class MaxNumber implements Serializable {
// constructors
public MaxNumber() {
}
/**
* Constructor for primary key
*/
public MaxNumber(String id) {
this.setId(id);
}
// primary key
private String id;//ID
public java.lang.String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
private String keyName; //主键
private Integer keyValue; //值
private String remark; //备注
public String getKeyName() {
if(keyName==null) return null;
else return keyName.trim();
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public Integer getKeyValue() {
return keyValue;
}
public void setKeyValue(Integer keyValue) {
this.keyValue = keyValue;
}
public String getRemark() {
if(remark==null) return null;
else return remark.trim();
}
public void setRemark(String remark) {
this.remark = remark;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int PRIME = 31;
int result = super.hashCode();
result = PRIME * result + ((id == null) ? 0 : id.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
final MaxNumber other = (MaxNumber) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
public String toString() {
StringBuffer sb = new StringBuffer("");
sb.append("MaxNumber{id=");sb.append(id);sb.append(",");
sb.append("keyName=");sb.append(keyName);sb.append(",");
sb.append("keyValue=");sb.append(keyValue);sb.append(",");
sb.append("remark=");sb.append(remark);
sb.append("}");
return sb.toString();
}
}
分享到:
相关推荐
cod4序列号生成器 cod4序列号生成器
然而,"Nero Burning Rom 序列号生成器"涉及的话题较为敏感,因为这可能指的是一个用于生成非法或假冒软件序列号的工具,这违反了软件版权法律。 首先,让我们明确一点:合法使用软件是每个用户的义务,尊重知识...
数据库序列号生成器是一种工具,通常用于为数据库中的记录生成唯一的标识符,这些标识符可以作为主键,确保数据的唯一性和完整性。在数据库设计中,主键是表中一个或一组字段,其值能够唯一地标识每条记录。序列号...
虚拟机序列号生成器.rar
易语言序列号生成器源码,序列号生成器,十六进制到十进制,十进制到十六进制,序列号显示,序列号算法,序列号反算,读取授权文件,秘密,wvsprintfA,StrToIntExA
【标题】"adobe-dreamwear cs4序列号生成器"所涉及的知识点主要集中在Adobe Dreamweaver CS4这款软件的授权与激活机制上。Adobe Dreamweaver是Adobe公司开发的一款专业级网页设计和开发工具,它融合了视觉设计与代码...
卡巴斯基序列号生成器
Steelray Project Viewer序列号生成器noy-steelray.project.viewer.keygen.jar 支持5.0以上版本,其他版本未测试 运行需安装jdk、jre 另,内含多个可用的license
web storm 序列号生成器, 破解, 支持当前最新的WebStorm3.03, 3.03版本支持NodeJS的开发. 非常好用的JavaScript的开发工具.
软件序列号生成器,通取物理地址,实现对MAC地址的绑定。加密好工具。
系统硬盘序列号 网卡MAC 主板信息 支持Windows10 1809 1903 1909 2004 20h2 21h1 使用方法就是依次输入1 2 3就行 重启不会自动恢复 : 随机化注册表和重置网卡数据 重启会自动恢复 : 加载匿名驱动 建议配合影子系统...
经过多次尝试和搜索,经本人验证确实可以完美破解Delphi和C++ Builder XE5的破解工具和序列号生成器。
使用序列号生成器违反了软件的许可协议,因为这本质上是逃避了购买授权的必要性。合法使用Balsamiq Mockups应通过官方渠道购买并激活,支持软件开发者的工作。 **3. 命令行运行** 描述中提到了在命令行环境下运行`...
在描述中提到的“vmware workstation 10 序列号生成器”是指一种工具,通常这类工具是非官方的,目的是为了生成可以激活VMware Workstation 10的序列号。然而,值得注意的是,使用这种序列号生成器是违反VMware的...
完全意义上的 汉语大辞典5.87序列号生成程序,不是程序的破解,真正的注册序列号,一台机器对应很多序列号,选择一个输入即可完美注册
本文将深入探讨MyEclipse 6.0和6.5版本的序列号生成器相关知识。 首先,我们需要理解软件序列号(Serial Number)的作用。序列号是软件开发商用于验证用户合法性的关键,通常每个软件安装后都需要输入正确的序列号...
【标题】"eclipse序列号生成器"涉及的是软件授权和Eclipse集成开发环境(IDE)的相关知识。Eclipse是一款开源、跨平台的Java IDE,广泛用于Java应用程序的开发,同时也支持其他编程语言如C++、Python等。然而,这里...
在IT领域,序列号生成器通常用于软件授权验证,它基于特定算法生成一串唯一标识符,以确保软件只能在合法授权的设备上运行。在这个初级代码项目中,开发者使用了Qt库,一个跨平台的应用程序开发框架,来实现硬件信息...
VMware是全球云基础架构和移动商务解决方案厂商,提供基于VMware的解决方案,企业通过数据中心改造和公有云整合业务,借助企业安全转型维系客户信任