生成顺序的流水号,用到同步方法,但还生成相同的流水号,同步方法是否有问题?

northenwolf1 2009-08-20 10:49:18
环境:jdk1.5.0.15 tomcat6.016 项目框架:hibernate、dwr
在保存记录时生成流水号,流水号构成LS200908000001RB(两位字母前缀+4位年份+2位月份+6位流水号+两位字母后缀)
保存记录的servlet关键代码如下:



…其它字段赋值略
result = cxRegDAO.saveCxReg(Sequence.saveReg(reg)); //保存的同时先生成流水号



生成流水号的类(即Sequence类)

package cn.ls.ggt.common;

import java.util.Calendar;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import cn.ls.ggt.config.Constant;
import cn.ls.ggt.dao.ICxRegDAO;
import cn.ls.ggt.model.cx.CxReg;
import cn.ls.ggt.servlet.cx.regcase.AddCase;
import cn.ls.util.DaoFactory;
import cn.ls.util.HibernateDAO;

public class Sequence extends HibernateDAO {
static Integer xhIndex;
private static Log log = LogFactory.getLog(Sequence.class);

static{
xhIndex = makeMaxSequence();
log.error("重新从数据库中取得序列号!");
}

//从数据库中取得最大流水号并赋给xh_index静态变量
/**
数据库表结构如下
Regid
…其它字段略
xh_index 当前的流水号,即最大的记录号
xh_year 当前年份,因为流水号为当年最大流水号,新的一年流水号从0重新记
*/
public static Integer makeMaxSequence(){
ICxRegDAO cxRegDAO = (ICxRegDAO)DaoFactory.getDao("cxRegDao");
Integer maxSequence = cxRegDAO.getMaxIndex();
//此方法生成的sql语句为:select top 1 xh_index from cx_reg where xh_year='2009' order by xh_index desc
if(maxSequence == null){
maxSequence = 0;
}
maxSequence++;
return maxSequence;
}

/**功能:生成最大序列号
* @return 数组中第一个元素为未补位的序号,第二个元素为补位后的序号
*/
public synchronized static String makeSequence(){
String result = null;
Integer maxSequence = 0;
if(xhIndex == 0){
maxSequence = makeMaxSequence();
xhIndex = maxSequence;
log.error("重新从数据库中取得序列号!");
}else{
maxSequence = xhIndex++;
}
log.error("xh xhIndex="+xhIndex+" maxSequence="+maxSequence);
String max = maxSequence.toString();
String tmp = "";
for(int i=max.length();i<6;i++){
tmp += "0";
}
result = tmp+max;
return result;
}

public static CxReg saveReg(CxReg cxReg){
String seqArr = Sequence.makeSequence();
log.error("saveReg sequence:"+seqArr);
Calendar c = Calendar.getInstance();

String xhYear = String.valueOf(c.get(Calendar.YEAR));
String xhMonth = (c.get(Calendar.MONTH)+1)<10?"0"+(c.get(Calendar.MONTH)+1):String.valueOf(c.get(Calendar.MONTH)+1);
String regID = Constant.STRART_WITH+xhYear+xhMonth+seqArr+cxReg.getConsignUnit();
cxReg.setXhYear(xhYear);
cxReg.setXhIndex(Integer.valueOf(seqArr));
cxReg.setRegId(regID); //完整流水号
log.error("saveReg end:"+regID);
return cxReg;
}
}

现在的问题是在高峰期生成的流水号(即xh_index)仍有重复,期间用日志输入信息跟踪,发现重复的流水号时,日志中竟然没有saveReg方法中的日志记录,即log.error("saveReg sequence:"+seqArr);
和log.error("saveReg end:"+regID);输出的信息

请问这个问题可能会出在哪里?同步法写的有问题吗?还是静态变量xh_index的问题?还是有其它因素?(项目中有用的dwr)
...全文
466 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
northenwolf1 2009-08-20
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 calvinsmith 的回复:]
makeSequence中的 重新从数据库取出序列是什么意思?

如果序列是预先储存在数据库中,那么这个序列就不应该是重复,如果这个序列是你生成的序列号的一部分且在库中并不会出现重复,那么怎么会在新生成的流水号中重复???
[/Quote]
重新从数据库取出序号是在服务器重启时从数据库中取出最大的流水号,并初始化静态变量,数据库记录如:
regid ...(其它列略) xh_index xh_year
ls200908000001RBrb 。。。 1 2009
ls200908000002RBrb 。。。 2 2009

在服务器启动后第一次取流水号是要从数据库中取,执行select top 1 xh_index from cx_reg where xh_year='2009' order by xh_index desc
把得到的最大流水号取出并赋给静态变量xh_index,以后再生成时就直接makeSequence()把xh_index自增1作为新的序列号,并组合成regid,并把这个序号同时保存到此条记录中!
现在的问题是在以后的操作中仍然有生成适于重复的xh_index至数据库记录中

calvinsmith 2009-08-20
  • 打赏
  • 举报
回复
makeSequence中的 重新从数据库取出序列是什么意思?

如果序列是预先储存在数据库中,那么这个序列就不应该是重复,如果这个序列是你生成的序列号的一部分且在库中并不会出现重复,那么怎么会在新生成的流水号中重复???
northenwolf1 2009-08-20
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 shi676879453 的回复:]
你这里用的是static的 根本就没东西给你锁
[/Quote]
我已经用public synchronized static String makeSequence()把整个Sequence类都锁定了呀!
northenwolf1 2009-08-20
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 majy 的回复:]

首先你的过程A取到一个最大值,执行其他事务,并没有插入到数据库中,这个时候,另一个过程B又在取最大值了,这时候,这两个序列是一样的
[/Quote]
A取到一个最大值,执行其他事务,并没有插入到数据库中,这时候B又在取最大值了,但A取完时已经对静态变量加1了,B在取的时候是取静态变量中的最大值,并没有到数据库中去取呀,这跟A有没有保存到数据库已经没有关系了吧!?
只有在服务器刚启动时才从数据库中取最大值,其它时候都是从内存中的static变量中取到的呀!
majy 2009-08-20
  • 打赏
  • 举报
回复
程序未必有问题,但是整个事务逻辑可能有问题:

首先你的过程A取到一个最大值,执行其他事务,并没有插入到数据库中,这个时候,另一个过程B又在取最大值了,这时候,这两个序列是一样的

如果是oracle,建议你的6位数的流水号通过 sequence来取
shi676879453 2009-08-20
  • 打赏
  • 举报
回复
你这里用的是static的 根本就没东西给你锁
yuzhangchen 2009-08-20
  • 打赏
  • 举报
回复
用单例模式吧。
lxxzhy 2009-08-20
  • 打赏
  • 举报
回复
把得到的最大流水号取出并赋给静态变量xh_index,以后再生成时就直接makeSequence()把xh_index自增1作为新的序列号,并组合成regid,并把这个序号同时保存到此条记录中!

在你的代码中,没看到哪里把xh_index放回数据库.那么下次重启web服务器,又拿到上次的所谓最大值,不就重复了么? 也许你应该在服务器关闭的时候把这个静态变量xh_index更新回数据库.(应该是用ServletContextListener吧)
northenwolf1 2009-08-20
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 shi676879453 的回复:]
锁定 xhIndex
[/Quote]
我已经用public synchronized static String makeSequence()把整个类都锁定了呀!难道还没有把xh_index锁定吗?
shi676879453 2009-08-20
  • 打赏
  • 举报
回复
锁定 xhIndex
网络咖啡 2009-08-20
  • 打赏
  • 举报
回复
public class Count{
int n;
static Count instance;
private Count(){
n = 0;
}
public static Count getInstance(){
if(instance == null){
instance = new Count();
}
return instance;
}
public synchronized int getN(){
n++;
return n;
}
}
calvinsmith 2009-08-20
  • 打赏
  • 举报
回复
你贴的代码逻辑上没错误


这样吧,如果你们公司的保密制度允许的话

重启服务器几次,然后把每次重启后生成的流水号,贴上来看看

记着一定要重启后的
northenwolf1 2009-08-20
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 calvinsmith 的回复:]
重复的xh_index????

我的理解是重启后从新取出序列号的索引值xh_index? RIGHT?

看你的流水号的组合公式,里面有6位的流水号,那么这个流水号是否能够重复,以16进制来表示的话,这个6位所能表示的范围是多少,是不是从这个上面再做做文章

还有那个索引值与生成的号码之间有没有显性的关系?
[/Quote]
流水号是字符类型啊!跟进制有什么关系?!!!
需要有6位的序列号,而序列为是1,就在前面补5个0就是6位,10的话前面就补4个0,依此类推,再加上前缀后缀就是完整的流水号喽,而且序列是每年归一次0,
完整的流水号和序列在数据库记录中分别有保存
如果一个流水号为ls200908000001rb则对应的数据库记录中regid(完整流水号)保存的就是ls200908000001rb,xh_index(序列)字段保存的为1,xh_year(年份)保存的为2009,说清楚了吗?!
calvinsmith 2009-08-20
  • 打赏
  • 举报
回复
重复的xh_index????

我的理解是重启后从新取出序列号的索引值xh_index? RIGHT?

看你的流水号的组合公式,里面有6位的流水号,那么这个流水号是否能够重复,以16进制来表示的话,这个6位所能表示的范围是多少,是不是从这个上面再做做文章

还有那个索引值与生成的号码之间有没有显性的关系?

62,621

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧