原理类似hibernate的主键生成机制,在系统启动时从数据库中读出最大的流水号,赋给一个类的静态变量,需要时,从该静态变量中取得流水号,加1后,就是你要的值。
代码:
public class testA {
public static Long maxNo
static {
synchronized(maxNo){
maxNo = select max(maxNo) from DB
}
}
public synchronized static long getMaxNo(){
return(++maxNo)
}
}
优点:保证同步,不会取到相同的值,同时避免了数据库的反复读取。
缺点:在cluster环境下
还是用上面的方法,但是只能在一台机器上发布并且绑定到JNDI,别的机器可以通过配置文件得知去哪里产生maxNo(实际上,每台机器都可以发布,但是所有机器只能去访问指定的一台机器),这样的效率虽然低了点,但是还是觉得比每次去数据库里取好点。当然,可以改进,就是再加一个本地类,每次去JNDI那里的类去取数据时,一次取20个回来(就是JNDI上面的类一次要加20),以后本地就用这20个值,用完了,再去JNDI上面的类去取。
当然,前面的这两个例子都比较复杂,甚至还有人提出用单态的方法。而我在.net中用的就比较简单了。方法如下:
在数据库(sqlserver)中新建一张表(sequence_num),专门用来生成流水号。
字段1:s_id,varchar(50),notnull
字段2:tmstmp,timestamp,notnull
代码如下:
public string getOderid()
{
private SqlConnection conn = null
private SqlCommand comm = null
private string s_id = ""
private byte [] tmstmp//时间戳类型在.net中对应为一个byte数组
private int s_idplus = 0
private string orderid = ""
try
{
conn = SqlConn.getConn()
conn.Open()
do
{
string sqlQuery = "select s_id,tmstmp as tmstmp from sequence_num"
comm = conn.CreateCommand()
comm.CommandText = sqlQuery
SqlDataAdapter da = new SqlDataAdapter(comm)
DataTable dt = new DataTable()
dt.Clear()
da.Fill(dt)
comm.Parameters.Clear()
if (!dt.Rows[0]["s_id"].ToString().Substring(0,12).Equals(DateTime.Now.ToString("yyyyMMddHHmm")))//每一分钟重置一次计数,实际上我认为每天重置一次比较好
{
s_id = DateTime.Now.ToString("yyyyMMddHHmm")+"0001"
tmstmp = (byte[])dt.Rows[0]["tmstmp"]
}
else
{
s_id = dt.Rows[0]["s_id"].ToString()
tmstmp = (byte[])dt.Rows[0]["tmstmp"]
s_idplus = Convert.ToInt32(s_id.Substring(12)) + 1
s_id = s_id.Substring(0,12)+Convert.ToString(s_idplus).PadLeft(4,'0')
}
string sqlUpdate = "update sequence_num set s_id = @s_id where tmstmp = @tmstmp"//保证在记录未被修改的情况下更新,如果更新不成功,则重走一遍生成序列号的流程
comm.CommandText = sqlUpdate
comm.Parameters.Clear()
comm.Parameters.Add("@s_id",SqlDbType.VarChar,50)
comm.Parameters["@s_id"].Value = s_id
comm.Parameters.Add("@tmstmp",SqlDbType.Timestamp)
comm.Parameters["@tmstmp"].Value = tmstmp
}
while(comm.ExecuteNonQuery()<=0)
}
catch(SqlException ex)
{
string aaa = ex.Message
}
finally
{
if(conn!=null)
{
conn.Close()
}
}
return s_id
}
这个流程都是在.net里实现的,实际使用中可以通过存储过程来实现。当然,这种方法对数据库的访问会比较频繁,另外,数据库表建立的时候必须插入一条初始值,这是代码不完善的地方,有时间的话我会去完善它。只是判断如果表内没有记录的情况下就插入一条初始记录,这个时候如何处理并发问题还没想明白。
用存储过程生成流水号是很常用的,这里以生成订单编号的流水号作为示例。(新的一天的流水号从1开始,如:今天的订单编号是CD2013010900014,下一个订单编号将是CD2013010900015;明天的订单编号将从CD2013011000001开始)生成规则:2位前缀+年月日+5位流水号 或者 2位前缀+年月日时分+5位流水号 或者 2位前缀+年月日时分秒+5位流水号。
测试订单表(test_orders):
[sql] view plain copy
CREATE TABLE `test_orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`orderNo` varchar(25) NOT NULL DEFAULT '',
`orderName` char(10) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=76 DEFAULT CHARSET=utf8
生成订单编号的存储过程(generate_orderNo):
[sql] view plain copy
CREATE DEFINER=PROCEDURE `generate_orderNo`(in orderNamePre char(2), in num int, out newOrderNo varchar(25))
BEGIN
DECLARE currentDate varCHAR (15) -- 当前日期,有可能包含时分秒
DECLARE maxNo INT DEFAULT 0 -- 离现在最近的满足条件的订单编号的流水号最后5位,如:SH2013011000002的maxNo=2
-- DECLARE l_orderNo varCHAR (25) -- 新生成的订单编号
-- DECLARE oldDate DATE -- 离现在最近的满足条件的订单编号的日期
DECLARE oldOrderNo VARCHAR (25) DEFAULT '' -- 离现在最近的满足条件的订单编号
if num = 8 then -- 根据年月日生成订单编号
SELECT DATE_FORMAT(NOW(), '%Y%m%d') INTO currentDate -- 订单编号形式:前缀+年月日+流水号,如:SH2013011000002
elseif num = 14 then -- 根据年月日时分秒生成订单编号
SELECT DATE_FORMAT(NOW(), '%Y%m%d%H%i%s') INTO currentDate -- 订单编号形式:前缀+年月日时分秒+流水号,如:SH2013011010050700001,个人不推荐使用这种方法生成流水号
else -- 根据年月日时分生成订单编号
SELECT DATE_FORMAT(NOW(), '%Y%m%d%H%i') INTO currentDate -- 订单形式:前缀+年月日时分+流水号,如:SH20130110100900005
end if
SELECT IFNULL(orderNo, '') INTO oldOrderNo
FROM test_orders
WHERE SUBSTRING(orderNo, 3, num) = currentDate
AND SUBSTRING(orderNo, 1, 2) = orderNamePre
and length(orderNo) = 7 + num
ORDER BY id DESC LIMIT 1 -- 有多条时只显示离现在最近的一条
IF oldOrderNo != '' THEN
SET maxNo = CONVERT(SUBSTRING(oldOrderNo, -5), DECIMAL) -- SUBSTRING(oldOrderNo, -5):订单编号如果不为‘‘截取订单的最后5位
END IF
SELECT
CONCAT(orderNamePre, currentDate, LPAD((maxNo + 1), 5, '0')) INTO newOrderNo -- LPAD((maxNo + 1), 5, '0'):如果不足5位,将用0填充左边
INSERT INTO test_orders (orderNo, orderName) VALUES (newOrderNo, 'testNo') -- 向订单表中插入数据
--set newOrderNo = l_orderNo
SELECT
newOrderNo
END
参数说明:orderNamePre:(输入)订单编号的前缀,这里设定为两个字符
num:(输入)将按什么规则生成流水号(生成规则有:年月日、年月日时分秒、年月日时分三种),可选的num有:8、12、14
newOrderNo:(输出)新生成的订单编号
生成中的一些说明在存储过程中已经写得很明确了,这里不再重复。
调用存储过程向表中插入数据:
[sql] view plain copy
SET @orderNo = ''
CALL `generate_orderNo`('SH', 12, @orderNo)
SELECT @orderNo
可以设计两个字段,或者是,
你写一个方法然后取出来你要的最大值+
1然后得到的值,
再做计算,
思路:
1.sql
select
max(流水号)
from
表名
2.比方你上面的,
用字符串截取,
获得去掉前两位的数,
然后获得年月日,
然后呢,
在你的序号上增加一
3.然后等到了你要的数据HK201301050001,
或者,
你建两个字段,
一个显示流水号(也就是你要的值),
一个就不显示,
是这个表的序列号,
然后根据这个序列号去生成你要的HK年月日+
序列号(也就是第二个字段),
然后得到你要的值,
上面的可以用一个方法实现,
然后呢,
就去调用这个方法就行了
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)