MySQL SEQUENCE iBATIS3 で。。。

MySQL で、Oracle SEQUENCE と同様の機能を作成する方法は、ネットで多く紹介されてる。
FUNCTION を用意してiBATIS3(myBatis) でシーケンス値取得を整理した。

MySQL シーケンスの代用テーブルを用意
CREATE  TABLE  sequence  (id  INT  UNSIGNED  NOT  NULL);

1行作成しておく。
INSERT  INTO  sequence  VALUES (0);

ファンクションを用意する

CREATE FUNCTION nextSequence() RETURNS INT
BEGIN
  UPDATE  sequence SET  id=LAST_INSERT_ID(id+1);
  RETURN  LAST_INSERT_ID();
END;

iBATIS3 SQLMap を用意
<select id="nextFooSequence" resultType="long">
   select nextSequence() from dual
</select>

nextSequence を実行するもの
synchronized メソッドである!
------------------------------------
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.google.inject.Inject;
/**
 * SQLMap の ID が示すシーケンス取得FUNCTION 実行する
 */

final class NextSequence{
   private SqlSessionFactory sqlsessionFactory;
   private String sqlmapId;
   @Inject
   public NextSequence(SqlSessionFactory sqlsessionFactory,String sqlmapId){
      this.sqlsessionFactory = sqlsessionFactory;
      this.sqlmapId = sqlmapId;
   }
   public synchronized long get(){
      SqlSession session = this.sqlsessionFactory.openSession();
      long id = (Long)session.selectOne(this.sqlmapId);
      session.close();
      return id;
   }
}

------------------------------------

さらにラップしてシングルトンで実行するようにする
------------------------------------

/**
 * シーケンス値取得
 */

public interface Sequence{
   public long next();
}

------------------------------------

import com.google.inject.Inject;
/**
 * シーケンス値取得実装.
 * NextSequenceをインジェクトして生成されるようにすること
 */

class SequenceImpl implements Sequence{
   private NextSequence nextsequence;
   @Inject
   public SequenceImpl(NextSequence nextsequence){
      this.nextsequence = nextsequence;
   }
   @Override
   public long next(){
      return this.nextsequence.get();
   }
}

ファクトリを用意する
------------------------------------
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Singleton;
/**
 * Sequence ファクトリ
 */

public final class SequenceAccessFactoy{
   private SequenceAccessFactoy(){}
   public static Sequence create(final String sqlId){
      final Injector injector = Guice.createInjector(new IBatisModule()
      ,new AbstractModule(){
         @Override
         protected void configure(){
            binder().bind(String.class).toInstance(sqlId);
         }
      });
      Injector injector2 = injector.createChildInjector(new AbstractModule(){
         @Override
         protected void configure(){}
         @SuppressWarnings("unused")
         @Provides @Singleton
         protected Sequence getSequenceGet(){
            return injector.getInstance(SequenceImpl.class);
         }

      });
      return injector2.getInstance(SequenceImpl.class);
   }
}

------------------------------------
実行は、、

   Sequence sequence = SequenceAccessFactoy.create("nextFooSequence");

   long val = sequence.next();
====================
シーケンス値を整数でなく文字列にするなら、FUNCTIONで文字列にする。
10桁なら、以下のように。

CREATE FUNCTION test.nextSequence() RETURNS CHAR(10)
BEGIN
   UPDATE sequence SET id=LAST_INSERT_ID(id+1);
   RETURN LPAD(CAST(LAST_INSERT_ID() AS CHAR),10,'0');
END;

iBATISのSQLMap も、resultType="long" でなく
resultType="string" にすればよい