JPAで@OneToMany
を複数定義して両者をjoin fetch
で取得しようとすると、下記のような実行時例外になることがある。
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [jpa.sample2.Tweet.reTweets, jpa.sample2.Tweet.favorites]
at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:76) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
at org.hibernate.loader.hql.QueryLoader.<init>(QueryLoader.java:110) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
状況
説明用のスキーマとして、一つのtweetに複数のReTweetと複数のFavorite、というテーブルを想定する。javaのentityとしては以下のような感じ。
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Data
public class Tweet {
@Id
Long id;
String value;
@OneToMany
@JoinColumn(name = "tweetId")
List<ReTweet> reTweets;
@OneToMany
@JoinColumn(name = "tweetId")
List<Favorite> favorites;
}
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Data
public class ReTweet {
@Id
Long id;
Long tweetId;
String value;
}
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Data
public class Favorite {
@Id
Long id;
Long tweetId;
String value;
}
JPQLは以下のようにjoin fetch
を2回使用する。
@Repository
public interface TweetRepository extends JpaRepository<Tweet, Long> {
@Query("select distinct t from Tweet t "
+ " left join fetch t.reTweets "
+ " left join fetch t.favorites"
+ " order by t.id")
List<Tweet> list2();
}
こうすると上記の実行時例外となる。
解決策
このケースの場合はList
をSet
に変えればよい。
@OneToMany
@JoinColumn(name = "tweetId")
Set<ReTweet> reTweets;
@OneToMany
@JoinColumn(name = "tweetId")
Set<Favorite> favorites;
またはMap
でも良い。
@OneToMany
@JoinColumn(name = "tweetId")
Map<Long, ReTweet> reTweets;
@OneToMany
@JoinColumn(name = "tweetId")
Map<Long, Favorite> favorites;
もしくは、単一のJPQLを諦めて複数に分割する、というやり方も考えられる。
@Query("select distinct t from Tweet t "
+ " left join fetch t.reTweets "
+ " order by t.id")
List<Tweet> list3();
@Query("select distinct t from Tweet t "
+ " left join fetch t.favorites"
+ " order by t.id")
List<Tweet> list4();