上篇《在Sybase数据库上模拟序列对象》在多线程环境下测试有重复问题。

 题记:IAM3.0版本默认的数据库是Oracle,最近在移植到Sybase中遇到问题,Sybase中没有序列,但是又不想修改java代码,如何在Sybase数据库上模拟序列对象呢?

一、创建以下对象
1、创建表SybaseSequences
SybaseSequences保存所有的序列
 
Create table SybaseSequences (
      SeqName nvarchar(255) primary key, -- name of the sequence
      Seed int  DEFAULT (1) not null, -- seed value
      Incr
numeric(1,0)  DEFAULT (1) not null, -- incremental
      Currval
numeric(22,0)
)
lock datarows---加上后可以支持行级锁
Go
2、自定义存储过程custom_proc_CreateNewSeq
custom_proc_CreateNewSeq用于添加一个序列,输入参数为SeqName
 
create procedure custom_proc_CreateNewSeq
      @SeqName nvarchar(255),
      @seed int = 0,
      @incr  NUMERIC  = 1
  --存储过程作用 :在Sybase数据库上模拟Oracle序列对象的创建
  --传入参数解释 :
  --SeqName 序列的名称
  --seed    初始值
  --incr    步长
  --开发人员   :宋亚坤
  --开发时间   :2013-01-15
  --修改人     :
  --修改事项   :
  --修改时间   :
 
as
begin
      declare @currval NUMERIC
      /*判断序列是否已经存在*/
      if exists (
            select 1 from SybaseSequences
            where SeqName = @SeqName )
      begin
            print 'Sequence already exists.'
            return 1   
      end
      /*判断初始值和步长并赋给默认值*/
      if @seed is null set @seed = 1
      if @incr is null set @incr = 1
      set @currval = @seed
      /*创建序列*/
      insert into SybaseSequences (SeqName, Seed, Incr, CurrVal)
      values (@SeqName, @Seed, @Incr, @CurrVal)
     
end
go
3、自定义存储过程custom_proc_GetNewSeqVal
custom_proc_GetNewSeqVal用于生成指定的序列的下一个值,输入参数为SeqName
 
create procedure custom_proc_GetNewSeqVal
      @SeqName nvarchar(255) IN
  --存储过程作用 :在Sybase数据库上模拟Oracle序列对象的nextval
  --传入参数解释 :
  --SeqName 序列的名称
  --开发人员   :宋亚坤
  --开发时间   :2013-01-15
  --修改人     :
  --修改事项   :
  --修改时间   :
as
begin
      declare @NewSeqVal NUMERIC
      set NOCOUNT ON
      /*根据序列的名称查询到当前序列并锁定当前行*/
    select CurrVal from SybaseSequences holdlock where SeqName = left(@SeqName,charindex('.',@SeqName)-1)
      /*更新当前序列的CurrVal并返回*/
      update SybaseSequences
      set @NewSeqVal = CurrVal+Incr,CurrVal = CurrVal+Incr
      where SeqName = left(@SeqName,charindex('.',@SeqName)-1)
      
      if @@rowcount = 0
      begin
        print 'Sequence does not exist'
        return
      end
      
      return @NewSeqVal
end
---由于传入值为XXX.nextval所以使用 left(@SeqName,charindex('.',@SeqName)-1)
二、验证:
1、创建一个usergroup_sequence序列
custom_proc_CreateNewSeq N'usergroup_sequence'
2、输出usergroup_sequence的当前值,模拟Oracle的nextval
Declare @NewSeqVal NUMERIC
Execute @NewSeqVal=custom_proc_GetNewSeqVal @seqname=N'usergroup_sequence'
3、查询
select * from SybaseSequences where seqname='usergroup_sequence';
 
三、产品中如何使用:
1、设置存储过程custom_proc_GetNewSeqVal为任意模式
Execute sp_procxmode custom_proc_GetNewSeqVal,anymode
2、创建TestSeq序列
custom_proc_CreateNewSeq  N'TestSeq'
3、ibatis配置文件修改
上篇博文中在执行存储过程后再查询虽然可以保证不修改java代码,但是多线程并发测试有重复。
<!-- sybase下根据序列名称获取nextval -->
<parameterMap id="paramMap" class="Map">
    
<parameter property="currval" jdbcType="VARCHAR" javaType="String" mode="OUT"/>
  
<parameter property="name" jdbcType="VARCHAR" javaType="String" mode="IN"/>
</parameterMap>
<procedure id="getSequence" parameterMap="paramMap" resultClass="String">
{? = call custom_proc_GetNewSeqVal(?)}
</procedure>
        <!-- Oracle下获取序列的nextval  -->
       <select id="getSequence" resultClass="String"
parameterClass="Map">
select $name$ from dual
</select>
 
四、总结
这种方法使用一个表来保存所有的应用程序所需的序列,每个序列是一个单独的记录,易于使用和维护,
重新修改后的直接调用过程的实现方式可以支持多线程并发,且不死锁。
注:目前测试100个线程每个线程获取100个序列值下正常。