スポンサーリンク
目次
JPAオブジェクトのLombokでStackOverflowErrorが発生
循環参照が発生しメモリリーク
JPA
+Lombok
を組み合わせて利用している際に、以下のようなJPAで取得した親子関係を持つEntityオブジェクトを標準出力や文字列化、エラーログ出力時にそのまま渡してtoString()
メソッドを呼び出した際にjava.lang.StackOverflowError: null
が発生する場合があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
package entities.account; import java.time.LocalDateTime; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Entity @Table(name = "accounts") @NoArgsConstructor @Data @Builder @AllArgsConstructor public class AccountEntity{ @GeneratedValue(strategy = GenerationType.IDENTITY) @Id private Long id; @OneToOne @JoinColumn(name = "id", referencedColumnName = "accountId") public AccountStatusEntity accountStatusEntity; @Column private String name; @Column private LocalDateTime createdAt; @Column private LocalDateTime updatedAt; @Column private LocalDateTime deletedAt; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
package entities.account; import java.io.Serializable; import java.time.LocalDateTime; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Entity @Table(name = "account_status") @NoArgsConstructor @Data @Builder @AllArgsConstructor public class AccountStatusEntity implements Serializable { @Id @OneToOne @JoinColumn(name = "accountId", referencedColumnName = "id") public AccountEntity accountEntity; @Column private AccountStatusEnum status; @Column private LocalDateTime createdAt; @Column private LocalDateTime updatedAt; } |
1 2 3 4 5 6 7 8 9 10 |
Caused by: java.lang.StackOverflowError: null at entities.account.AccountStatusEntity.toString(AccountStatusEntity.java:21) at java.base/java.lang.String.valueOf(String.java:2951) at entities.account.AccountEntity.toString(AccountEntity.java:22) at java.base/java.lang.String.valueOf(String.java:2951) at entities.account.AccountStatusEntity.toString(AccountStatusEntity.java:21) at java.base/java.lang.String.valueOf(String.java:2951) at entities.account.AccountEntity.toString(AccountEntity.java:22) at java.base/java.lang.String.valueOf(String.java:2951) ...(以下無限ループ) |
ログを見てもらうとわかる通り、toString
を実行した際に依存している子モデルのtoString
が呼ばれ、その中のさらに依存している親モデルのtoString
が呼ばれ・・・という形で無限ループしてしまっています。
解決方法
@ToString.Excludeを使う
この現象を避けるには、子Entity側のtoString
で親EntityのtoString
を呼ばないようにする必要があります。
幸い、Lombok
では上記を実装するための便利なアノテーションがあるのでそちらを利用しましょう。
toString
から除外したいフィールドに以下をつけるだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package entities.account; import java.io.Serializable; import java.time.LocalDateTime; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; // ★追加 @Entity @Table(name = "account_status") @NoArgsConstructor @Data @Builder @AllArgsConstructor public class AccountStatusEntity implements Serializable { @ToString.Exclude // ★追加 @Id @OneToOne @JoinColumn(name = "accountId", referencedColumnName = "id") public AccountEntity accountEntity; @Column private AccountStatusEnum status; @Column private LocalDateTime createdAt; @Column private LocalDateTime updatedAt; } |
これで循環参照がされなくなるので、正常にtoString
メソッドを呼び出す事が可能になりました。
@ToString(exclude = {“XXX”})は非推奨
ちなみにクラスに対して@ToString(exclude = {"除外対象の親Entityプロパティ名"})
とする事でも同様の処理をする事が可能だが、こちらの記述方法は非推奨のためいずれ無くなるらしいので使わないことをオススメします。
本日のオススメ商品
Anker 大容量モバイルバッテリー
ロゴス(LOGOS) 送風機 BBQガンブロー
Anker モバイルバッテリー搭載 USB急速充電器
日清 カップヌードル
GoPro HERO9 Black
烏龍茶 2L x 9本
マンハッタンポーテージ カジュアルバッグ
終わりに
以上のように循環参照の恐怖を知っていないと、サーバーが落ちてしまう原因の一つとなってしまいます。
LombokとJPAを組み合わせて利用する可能性がある方は是非覚えておくと良いでしょう♪