Doma入門 - 注釈処理

## はじめに

Domaの一番の特徴であり、鬼門でもあるのが注釈処理（アノテーションプロセッシング）です。

最新のDoma 2.60.0を前提に記述しますが、基本的にはどのバージョンにも当てはまる内容となっています。

Domaの他の機能紹介については[Doma入門](https://qiita.com/nakamura-to/items/deaff05ba530317f3cf8)もお読みください。

## 注釈処理とは

[注釈処理](https://jcp.org/en/jsr/detail?id=269)を使うと、コンパイル時にソースコードに付与されたアノテーションを読み取って、別のソースコードやバイトコードを生成できます。

Domaはソースコードを生成するタイプのフレームワークですが、例えば[Micronaut](https://micronaut.io/)はバイトコードを生成しています。

利用者視点で見ると、ソースコードを生成するタイプはコードを読んだりブレイクポイントを置いたりできるのでデバッグしやすく、バイトコードを生成するタイプはコンパイル時間をスキップできるという特長があります。

## 注釈処理の利点

利点を大別すると3つあるのではと思います。

1. ボイラープレートコードを削減できる
2. コンパイル時にエラーを検出できる
3. 実行時のブートストラップ（初期化）を高速化できる

### ボイラープレートコードを削減できる 

ボイラープレートコード削減は注釈処理に限った利点ではなく、例えばリフレクションなどでも実現できます。この点に関しては、[AutoValue](https://github.com/google/auto/tree/master/value)というプロダクトが、注釈処理と他の手法を比較していて参考になります。特にこの[スライド](https://docs.google.com/presentation/d/14u_h-lMn7f1rXE1nDiLX0azS3IkgjGl5uxp5jGJ75RE/edit#slide=id.g2a5e9c4a8_00)が面白いです。

AutoValueのスライドでは触れられていませんが、リフレクションに限って言えば、ネイティブイメージ化を考慮する場合においては注釈処理の利点は大きいと言えます。ネイティブイメージを作るにあたっての[リフレクションの制約](https://github.com/oracle/graal/blob/master/substratevm/Reflection.md)を回避できるからです。

### コンパイル時にエラーを検出できる

この点については「[Domaの開発で大切にしている10のこと](https://qiita.com/nakamura-to/items/099cf72f5465d0323521)」という記事の[動かさないとわからないを減らす](https://qiita.com/nakamura-to/items/099cf72f5465d0323521#1-動かさないとわからないを減らす)で紹介しました。

### 実行時のブートストラップの高速化

なぜ、実行時のブートストラップの高速化が可能かというと、注釈処理を使わなければ実行時に生成してメモリに保持するであろうメタデータをコンパイル時にクラスとして生成できるからです。

ブートストラップに比較的時間がかかるDBアクセスフレームワークの代表格はHibernateでしょう。Hibernateは、SessionFactory（JPAとして使う場合はEntityManagerFactory）を作るのにエンティティクラスの情報をリフレクションで読み取り様々な処理をします。エンティティクラスの数が少ない場合は全く気にならないと思いますが、数が多くなってくると顕著に初期化に時間がかかることに気づくでしょう。ログレベルをtraceなどにするとどんなことをしているのがよくわかると思います。

[QuarkusのHibernate Extension](https://quarkus.io/guides/hibernate-orm)ではその辺りの対策されているかもと思ってエンティティ100個くらい作ってサンプル動かしましたが、起動までに数十秒かかったので特に対策は入っていなさそうでした（JVM modeで試したのですが、Native modeならまた違ってくる可能性があります）。

一方、Domaの場合、実行時にはすでに存在するクラスをロードするだけで必要なメタデータを入手できます。しかも、ブートストラップ時に一度にロードするのではなく、必要に応じてクラスをロードするようになっているためエンティティクラスの数が多くなってもブートストラップ時間が伸びるということはありません。

## 注釈処理で気をつけたいこと

最も気をつけたいのは、注釈処理を有効にするためにIDEが特殊な設定を要求することです。また、ビルドツールにMavenを使うかGradleを使うかによっても気をつけるところが変わってきます。

冒頭で鬼門と書きましたが、Domaが動かないという現象のほとんどがこの設定に関することのように見受けられます。

### おすすめのIDEとビルドツール

IDEでは、EclipseとIntelliJ IEDAのどちらでも動作確認しています。どちらが良いかは完全に好みですが、注釈処理を動かすにはEclipseの方が気を付けないといけないことが多いです。ただ、コツをつかめば大したことはないですし、Eclipseのインクリメンタルコンパイルによる注釈処理の実行はIDEAを使った場合に比べて迅速なフィードバックなので、気をつけるポイントが多いからという理由だけでEclipseを切り捨てるのは惜しいと思います（IDEAの場合は明示的にビルドしないと注釈処理が動かないが、Eclipseの場合はファイルをセーブするたびに動き、注釈処理によるエラーが迅速に開発者にフィードバックされます）。

ビルドツールは、GradleとMavenで動作確認しています。これもどちらでも好きな方を使っていただけます。

なお、<font color="red">GradleやMavenを使わずにEclipseやIEDAで動作させることも可能ですが全くおすすめしません</font>。依存ライブラリをはじめ様々な設定で絶対にハマるのでGradleかMavenのどちらかは必ず使ってほしいところです。

まとめると、こんな感じになります。

| IDE     | ビルドツール | 説明                                      |
|---------|------------|------------------------------------------|
| Eclipse | Gradle     | [Eclipse + Java + Gradle の環境で Doma を動かす](https://qiita.com/nakamura-to/items/9e05fe00be9d4d629fdc)を参照。                               |
| Eclipse | Maven      | [Eclipse + Java + Maven の環境で Doma を動かす](https://qiita.com/nakamura-to/items/f98a05b926363016070b)を参照。 |
| IDEA    | Gradle     | GradleプロジェクトとしてインポートすればOK。   |
| IDEA    | Maven      | MavenプロジェクトとしてインポートすればOK。    |

表の中の説明からリンクを張っていますが、Eclipseの利用については別記事を参照ください。

domaframeworkオーガナイゼーションにはGradleを使ったサンプルプロジェクトがあります。

- https://github.com/domaframework/getting-started
- https://github.com/domaframework/simple-examples

これらはEclipseでもIDEAでも動作します。
build.gradle.ktsファイルの書き方などを参考にしていただければと思います。

## おわりに

Domaの注釈処理について説明しました。

最近ZulipというチャットサービスにDoma専用のルームを開設しました。もしDomaに関して質問などあればこのチャットルームでお気軽にどうぞ。

- https://domaframework.zulipchat.com/

例えば、注釈処理の設定をGradleのビルドスクリプトに記述する方法などは世の中に情報少なめなので困ることあるかもしれませんが、質問もらえればアドバイスできそうです。

もちろん、DomaやDomaの関連プロダクトの開発に協力いただける方や情報を共有いただける方の参加もお待ちしています。