【SpringBoot】【MyBatis】SQLのシーケンスで自動生成されるデータをEntitiyに格納する方法

【SpringBoot】【MyBatis】SQLのシーケンスで自動生成されるデータをEntitiyに格納する方法

SQLのシーケンスを利用して、データをINSERTする時に主キーなどを自動で割り当てるといった時に、割り当てた値をEntityに格納したい場合の方法です。

前提

INSERT対象テーブル情報

postgresqlのチュートリアルテーブル群を使用しています。

テーブルデータ(Addressテーブル)

address_idが主キーで、今回シーケンスによって自動で値を割り当てます。

シーケンスデータ

CREATE SEQUENCE public.address_address_id_seq
	INCREMENT BY 1
	MINVALUE 1
	MAXVALUE 9223372036854775807
	START 1
	CACHE 1
	NO CYCLE;

実装

プロダクトコード

Address.java

INSERTするEntityです。

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Address {

    int addressId;
    String address;
    String address2;
    String district;
    int cityId;
    String postalCode;
    String phone;
    LocalDateTime lastUpdate;
}

AddressMapper.java

Mybatisを利用してAddressテーブルにデータをINSERTします。
アノテーションベースでの実装です。

@Mapper
public interface AddressMapper {


    @Insert("INSERT INTO address (" +
            "address_id," +
            "address," +
            "address2," +
            "district," +
            "city_id," +
            "postal_code," +
            "phone," +
            "last_update" +
            ")" +
            "VALUES (" +
            "address_address_id_seq.NEXTVAL," +
            "#{address.address}," +
            "#{address.address2}," +
            "#{address.district}," +
            "#{address.cityId}," +
            "#{address.postalCode}," +
            "#{address.phone}," +
            "#{address.lastUpdate}" +
            ")")
    @Options(useGeneratedKeys = true, keyColumn = "address_id", keyProperty = "address.addressId")
    void save(@Param("address") Address address);
}

主キーであるaddress_idを、シーケンスを使用して値を設定しています。

@Optionsで指定しているのが自動生成されたaddress_idをEntityのaddressに格納する設定です。

useGeneratedKeys・・・データベース側で自動生成されたキーを取得する。実際にはJDBCのgetGeneratedKeysメソッドを使用している。
keyColumn・・・テーブル内で自動生成が設定されている列名。カンマ区切りで複数指定対応。
keyProperty・・・keyColumnで指定された列名をここで指定したプロパティにセットする。カンマ区切りで複数指定対応。

テストコード

AddressMapperTests.java

private AddressMapper target;

@DisplayName("1件Insertし、シーケンスによって自動生成された値を検証する")
        @Test
        void test1Insert() {

            //GIVEN
            Address actualCase = Address.builder()
                    .address("613 Korolev Drive")
                    .district("Shibuya")
                    .cityId(1)
                    .phone("28303384290")
                    .lastUpdate(LocalDateTime.of(2015, 5, 2, 8, 50))
                    .build();

            //WHEN
            target.save(actualCase);

            //THEN
            assertThat(actualCase.getAddressId()).isEqualTo(1);

            int expect = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM address", Integer.class);
            assertThat(expect).isEqualTo(1);

        }

INSERTするデータをセットしているactualCaseで、主キーであるaddressIdがセットされていません。(intなので初期値0が入ります。)

target.saveメソッドを実行後、addressIdを1でassertしています。

つまり、メソッド実行後にactualCaseの中に自動生成されたaddressIdが格納されたということです。

備考

@SelectKeyアノテーションを使う方法

@Optionsで取得する方法とは別に、@SelectKeyで自動生成する方法もある。

その場合はこのようになる。

AddressMapper.java

    @Insert("INSERT INTO address (" +
            "address_id," +
            "address," +
            "address2," +
            "district," +
            "city_id," +
            "postal_code," +
            "phone," +
            "last_update" +
            ")" +
            "VALUES (" +
            "#{address.addressId}," +
            "#{address.address}," +
            "#{address.address2}," +
            "#{address.district}," +
            "#{address.cityId}," +
            "#{address.postalCode}," +
            "#{address.phone}," +
            "#{address.lastUpdate}" +
            ")")
    @SelectKey(statement = "SELECT address_address_id_seq.NEXTVAL",
            keyProperty = "address.addressId",
            before = true,
            resultType = Integer.class)
    void save(@Param("address") Address address);

@SelectKeyでシーケンスをSELECTして、事前にaddressIdに自動生成された値を格納させて、それを利用してINSERTしている。

なので、INSERTのVALUES句のaddress_idの部分も変わっている

  • @Optionsの方法…SQL側で自動生成させ、SQLが実行された後で値をEntityに格納する。
  • @SelectKeyの方法…INSERTより前にSELECTによってシーケンスから自動生成された値をEntityに格納し、そのデータをINSERTしている。

という感じ。
@SelectKeyだとSQLが一つ多く発行されるということですね。

参考

Mybatis公式

mybatisカテゴリの最新記事