MySQL+MyBatisでシーケンス(連番)を扱う


MySQLってシーケンスオブジェクトないんですね・・。auto_increment列使えよ〜ということなのかな?
軽くググってみると・・・LAST_INSERT_ID関数を使うとシーケンスっぽいことできるよ〜という記事がちらほら見つかりました。どうやら、この方法は公式のドキュメントでも紹介されている方法のようです。

検証バージョン

  • MySQL 8.0
  • MyBatis 3.5.6

テーブルを作る

create table sequence (
  name varchar(50) primary key,
  value   bigint
) ENGINE=MyISAM;

NOTE:

ENGINE=MyISAMにするとロールバックの影響を受けない(=ロールバックしても番号が戻らない)ようにすることができる見たいです(MyISAMがトランザクションをサポートしていないからのようです)。

Mapperを作る

@Mapper
public interface SequenceMapper {

  @Insert("INSERT INTO sequence (name, value) VALUES(#{name}, LAST_INSERT_ID(1)) " +
      "ON DUPLICATE KEY UPDATE value = LAST_INSERT_ID(value + 1)")
  @SelectKey(statement = "SELECT LAST_INSERT_ID()", keyProperty = "value", before = false, resultType = long.class)
  void incrementAndGet(Sequence sequence);

  default long nextSequence(String name) {
    Sequence sequence = new Sequence();
    sequence.name = name;
    incrementAndGet(sequence);
    return sequence.value;
  }

  class Sequence {
    private String name;
    private long value;
  }

}

シーケンス値を取得する

@SpringBootTest
class MyTest {

  @Autowired
  SequenceMapper sequenceMapper;

  @Test
  void test() {
    Assertions.assertThat(sequenceMapper.nextSequence("file_id")).isEqualTo(1L);
    Assertions.assertThat(sequenceMapper.nextSequence("file_id")).isEqualTo(2L);
    Assertions.assertThat(sequenceMapper.nextSequence("order_id")).isEqualTo(1L);
    Assertions.assertThat(sequenceMapper.nextSequence("order_id")).isEqualTo(2L);
  }

}

参考サイト