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" にすればよい