😨

EntityGraphのバグに遭遇した話

2024/12/18に公開

何が起こったか

Spring Bootを2.7から3.1にアップグレードしたところ、EntityGraphを使用した場合の動作が変化し、期待通りの結果が取得できなくなりました。
具体的には、不要な2重のJOIN がクエリ内で生成される問題が生じました。

Spring Bootのアップグレードに伴い、Hibernateのアップグレードも必要だったので、あわせてHibernateを5.6.1から6.4.0に変更しました。この変更が問題の要因となったようです。

EntityGraphとは

EntityGraphは、JPAの機能で、エンティティを取得するときに関連エンティティの取得戦略を動的に制御するための手段です。これにより、N+1問題を回避したり、必要なデータだけを効率よく取得することができます。
https://spring.pleiades.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/EntityGraph.html

本記事のケースでは、Childテーブルのsexを条件にフィルタリングし、データ取得することを目的として使用しています。

データ構造とクエリ

以下のようなデータ構造のエンティティがあります。

Parent.java
@Entity
public class Parent {
    @Id
    private Long id;

    @OneToMany(mappedBy = "parent")
    private List<Child> children;
}
Child.java
@Entity
public class Child {
    @Id
    private Long id;

    private int sex ;

    @ManyToOne
    private Parent parent;
}

この Parentエンティティに紐づく Childエンティティを特定の条件(sex)でフィルタリングするために、EntityGraphを使用してクエリを作成していました。

ParentRepository.java
@EntityGraph(attributePaths = {"children"})
  List<Parent> findByIdInAndChildrenSex(List<Long> ids, int sex);

アップグレード前は、Parentエンティティに紐づくChildエンティティがsexでフィルタリングされ必要なデータのみ取得できていました。

SELECT ...
FROM parent p
LEFT JOIN child c1 ON p.id = c1.parent_id
WHERE p.id in (?, ?)
AND c.sex = ?

ところが、アップグレード後にはchildを2重に結合したクエリが生成されるようになりました。

SELECT ...
FROM parent p
LEFT JOIN child c1 ON p.id = c1.parent_id
LEFT JOIN child c2 ON p.id = c2.parent_id
WHERE p.id in (?, ?)
AND c.sex = ?

その結果、予期しないデータを取得するようになったことで集計が倍増するなどの問題が発生しました。

原因

以下の報告によるとこの問題は確認されており、Hibernate6.4.3で修正されているようです。
https://hibernate.atlassian.net/browse/HHH-17629

この問題の原因は、Hibernateが5系から6系へのバージョンアップで内部処理に変更が加えられたことのようです。
これにより、EntityGraphを使用したクエリ生成で以前のバージョンでは起きなかった問題が新たに発生しました。

対応したこと

この問題への対応として、@Queryを使用して独自のクエリを定義する方法を採用しました。
これにより、不要な2重結合を回避し期待通りの結果を取得できるようになりました。

ParentReporitory.java
@Query("SELECT p FROM Parent p JOIN p.children c WHERE p.id IN :ids AND c.sex = :sex")
List<Parent> findByIdInAndChildrenSex(List<Long> ids, int sex);

終わりに

Spring BootやHibernateをアップグレードする際には、期待する動作が変更されることがあるため注意が必要です。同じような問題に遭遇された方の参考になれば幸いです。

BABY JOB  テックブログ

Discussion