@JsonIdentityInfoとは?
Jackson(=JavaオブジェクトをJSONに変換するライブラリ)用のアノテーションで、オブジェクト同士が循環参照しているとき、無限ループにならないようにするために使う。
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
これをエンティティにつけると、Jackson は次のように動作します:
- 初回出現時 → フルオブジェクトでシリアライズ
- 2回目以降の出現 → "id"だけの値(あるいは参照)として出力
項目 | 意味 |
---|---|
generator = PropertyGenerator.class | オブジェクトのプロパティ(ここではid)を使って識別する |
property = "id" | idプロパティを使う(Word.idなど) |
@JsonIdentityInfoなしの場合
WordA → WordB → WordA → WordB → WordA → ・・・
無限に繰り返して、 StackOverflowError(エラー)になる。
@JsonIdentityInfoを付けた場合
WordA (id: 1) → WordB (id:2) → 参照(id:1) → 参照(id:2) → ...
idだけで参照を表現する。無限ループしない。
例:
{
"id": 1,
"wordName": "A",
"relatedWords": [
{
"id": 2,
"wordName": "B",
"relatedWords": [1] ← 循環回避のため、idだけで参照
}
]
}
JSON変換時に
- まだ初めて出てくるオブジェクトなら展開して出力
- 2回目以降に出てきたオブジェクトならidだけ参照
という挙動になる
有効なケース
状況 | 内容 |
---|---|
内部的に相互参照のあるエンティティ構造をそのままJSONに出したいとき | 再帰的構造や親子関係を維持したままクライアントに返したい場合、@JsonIdentityInfoは便利です |
デバッグや一時的なプロトタイプ | エンティティ構造をフルで見たい、試したい、開発初期などに役立ちます |
クライアント側(JSなど)が、参照関係やID解決のロジックを持っている場合 | つまり「idだけ渡しても、その先を別途取得できる構成」の場合 |
有効でないケース
問題 | 説明 |
---|---|
フロントエンドがidだけのデータでは困る場合 | たとえば relatedWords[i].wordName を表示したいのに、それが 117 という数値だけだったら undefined になる |
JSONが「ID参照形式」になり、フロントで追加の解決処理が必要になる | クライアント側で ID → フル情報 のマッピングを自分で行う必要がある |
ネストが深くなると人間が読みにくくなる* | 入れ子の中が一部オブジェクト、一部IDだけという不安定な構造になりやすい |
結局エンティティの構造がそのままAPI仕様になってしまう | エンティティ設計変更のたびにAPIが壊れるリスクがある(DTOなら防げる) |
本番用途には非推奨 | JSON形式が不安定であり、堅牢さに欠ける |
使ってよい場面/避けるべき場面
使ってよい(有効) | 避けるべき(限界) |
---|---|
試作段階のREST APIでとりあえず動かしたい | 本番APIで安定したレスポンス構造が必要な場合 |
内部ツール・非公開APIなどで利用 | 外部公開するAPIや他システム連携時 |
フロントが id を使って後から詳細を取りに行く構成になっている |
フロントがそのまま表示に使いたいとき(例:関連語の名前をそのまま表示) |
結論
-
@JsonIdentityInfo
は、*PAの循環参照を一時的に避けるための応急処置には有効 - ただし、「idだけで参照」となるので、フロントが直接
.wordName
や.category.name
を使いたいときには非常に使いにくい - API設計として安全・堅牢にしたいなら、DTOに変換して返すのがベスト