takeda_san’s blog

KotlinとVRを頑張っていく方向。

Doma2のinsertで採番されたIDを戻り値で取得したい

きっかけ

Doma2だとinsertの戻り値でシーケンス採番後のエンティティが取得できる。
これがあると何がうれしいかというと、親子関係のエンティティで親のデータを挿入した際のDBで採番されたIDを利用して子のデータを作るみたいなことが簡単にできる。

なんだけれども、なんだか採番されたIDが返ってこなくて困ったので正しい書き方をメモ。

問題のコード

DAOはこんな感じ。
外部ファイルのinsert文を実行するSQL
この戻り値でDBで採番されたIDが返ってくるはず。
(エンティティがイミュータブルの場合は戻り値で帰ってくる)

@Dao
public interface PersonDomaDao {
    @Insert(sqlFile = true)
    Result<PersonEntity> insert(PersonEntity person);
}

insert文はこんな感じ。
idは自動採番なので除外したSQL文にした。

INSERT INTO person(
  name,
  age
)
VALUES
( 
  /* name */'hoge',
  /* age */1
)

Entityはこんな感じ。
主キー予定のものに GeneratedValue をつけてIDを採番する。

@Entity(immutable = true)
@Table(name = "person")
data class PersonEntity(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Int,
        val name: String,
        val age: Int
)

このとき、idはnull許可じゃないので、初期値として適当に値(例えば12345とか)を入れておく。
(かなりお行儀が悪いが…)
んで、戻り値を見てみると、idに初期値として設定した値(12345)が返ってくる。
しかしDB側を見てみると、問題なく正しいシーケンス番号が登録されている。

何かが、おかしい…

解決編

うーん、よく考えるといらないことをいろいろしている。
その辺をただせばうまく動きました。

原因その1

毎度ながら公式を見なさいという事案でした。
(ドキュメントが最近英語対応中っぽく、ja->enでURLが違うので検索結果から入ると軒並みNotfoundになってる気がする)

Entity classes — Doma ドキュメント

これの通り、IDには null もしくは 0未満の値 を設定してinsertに渡してあげないといけないのでした。
なのでこんな感じに直したら無事採番されました。

@Entity(immutable = true)
@Table(name = "person")
data class PersonEntity(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Int? = null,
        val name: String,
        val age: Int
)

Kotlinのベストプラクティスのところでもidがnull許可になっててなんでやねんと思ってたけど、そういうことだったんですね…
Kotlin サポート — Doma ドキュメント

原因その2

外部のSQLファイルを使っていると採番されない。
これはちゃんと調べてないので、もしかしたら採番する方法があるのかもしれないです。

@Dao
public interface PersonDomaDao {
    @Insert
    Result<PersonEntity> insert(PersonEntity person);
}

エンティティのidをnull許可にすることで、idをわざわざなくしたお手製SQLファイルは不要になる。
なんだかコード的にもスッキリ。
これがKotlinのベストプラクティスの力…