我要实现一个生成唯一序列号 的功能,日期加上从0开始的递增数字组成,比如200811110001,我需要使用数据库记录这个当前递增值,使用mysql 数据库,建立一张code表,由 日期字段,地增值字段组成。
程序使用spring 和hibernate,配置文件里面事务隔离级别我没设置,采用默认隔离级别,事务传播方式,我设置的是save的时候PROPAGATION_REQUIRES_NEW,其它情况PROPAGATION_REQUIRED。生成序列号部分我已经做了同步:   public  String saveCode() {
            //保证线程同步
    synchronized (CodeGenerationImpl.class) {

       System.out.println("thread "+Thread.currentThread().getId()+" saveCode(): start");
date = new OokjDate(new Date()).toString();
date = date.substring(2);
String[] sString = date.split("-");
date = "";
for (int i = 0; i < sString.length; i++)
date += sString[i];
/**
List<CodeLog> list = basicService
.find("from CodeLog where charactersign='" + character
+ "' and dateTime='"
+ new OokjDate(new Date()).toString() + "'");
codeLog = null;
Integer lastNumber;
if (list.size() != 0) {
codeLog = list.get(0);
lastNumber = codeLog.getLastNumber();
lastNumber++;
codeLog.setLastNumber(lastNumber);
basicService.update(codeLog);
} else {
lastNumber = new Integer(1);
codeLog = new CodeLog();
codeLog.setOokjDate(new OokjDate(new Date()));
codeLog.setCharacterSign(character);
codeLog.setLastNumber(lastNumber);
basicService.save(codeLog);
}
**/
Integer lastNumber=new Integer(start);
start++;

if (lastNumber < 10)
index = "000" + String.valueOf(lastNumber);
else if (lastNumber < 100)
index = "00" + String.valueOf(lastNumber);
else if (lastNumber < 1000)
index = "0" + String.valueOf(lastNumber);
else
index = String.valueOf(lastNumber);
System.out.println("thread "+Thread.currentThread().getId()+" saveCode(): end");
return character + date + index;
}
 }
里面的lastNumber是当前的序列号值,被我注释掉的部分是从数据库读取,然后加一然后在保存回数据库的过程,我用静态变量读写的方式替换测试,没有问题   Integer lastNumber=new Integer(start);
start++;start是我定义的类静态变量,测试过程中没有问题,生成序列号时没有重复的,但是使用数据库方式就会有重的,数据库读取部分,采用HQL 查询方式,由service调dao层操作数据库。控制台打出结果类似:线程1:
   0001
   0002
   0003
   0004
   0005
   。。
线程2:
   0002
   0003
   0004
   0005
 线程1先启动,线程2启动后就开始重了,而且是2里面的每个都和1重,不知道这个是不是session对象的原因。我原来怀疑是事务原因,后来发现那张表是MyISAM,所以怀疑是hibernate原因,不知道各位有什么见解,帮忙分析一下。

解决方案 »

  1.   

    注意:
     如果mysql类型是MyISAM 的话,不支持事务处理等高级处理
      

  2.   

    顺便说一下,如果是四位流水,完全不用那么麻烦吧
    Integer lastNumber=new Integer(start);
            start++;
            
            if (lastNumber < 10)
                index = "000" + String.valueOf(lastNumber);
            else if (lastNumber < 100)
    ......
    完全可修改为
    ((10000+lastNumber)+"").substring(1)
      

  3.   


    你说的同步方式是错误的,我的同步是没有问题的,而且我用静态变量的方式测试过了。现在只有用数据库的时候才会有问题,所以怀疑是hibernate的问题。我怀疑是不是我在一个线程里面循环调用saveCode方法,使用的是同一个HibernateSession,他就第一次时读了一次数据库,其它都是从这个session里面读取的,这样才导致的,比如线程1从 数据库读取的值是1,存入Session中,以后都是对这个操作。这样导致它们每个线程生成的编号都是连续的,没有间隔跳跃,但是重号。假如真是这个原因,还有一个问题,假如数据的最后值是99,线程1先运行,它产生的编号是 100 ,101,102,103,几秒钟后,我再启动线程2 ,线程2 产生的编号是 102,103,104,线程2 后运行,编号顺序不是从100开始,说明它读取到了线程1对数据库的修改后的值,这个问题怎么解释呢。有没有对Heibernate有研究的高手分析一下原因。
      

  4.   

    建议关键字:
    spring 单例
    Hibernate 脏
      

  5.   

    我想可能问题在这:service做了同步,但是提交事务不是service提交,而是spring的aop代理在提交事务,这时候同步已经失效。
    所以在此时有可能service执行完毕,但是事务没有被spring提交的时候,线程二读到了脏数据
    做如下改动后测试一下:
    hibernate的autocommit设置成true,hibernate.connection.autocommit=true
    然后再dao中的save或update的时候,后面加上session.flush();