こんな構造のテーブルがあるとする。
Spring Data JPAを使ってちょっと複雑なクエリを発行したい場合にSpecificationを使うが、existsを使う場合は、次のようなEntity構造だと、問題が発生した。(setter/getterは省略)
ちなみに、Spring Bootのバージョンは1.5.4.RELEASE。
User.java
@Entity
public class User {
@Id
private String userId;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Phone> services;
}
Phone.java
@Entity
public class Phone {
@Id
private Integer phoneId;
@ManyToOne
@JoinColumn(name = "USER_ID")
private User user;
private Integer type;
private String number;
}
これらのEntityを用いて、「Phoneが1つでもあるUserを取得」するためのexists句を
public interface UserRepository extends CrudRepository<User, Integer>, JpaSpecificationExecutor<User> {
static Specification<User> existsPhone() {
return (root, query, cb) -> {
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<Phone> subRoot = subquery.from(Phone.class);
return cb.exists(subquery.select(cb.literal(1)).where(
cb.equal(root, subRoot.get("user").get("userId")));
};
}
}
のように作って
userRepository.findAll(Specifications.where(UserRepository.existsPhone()));
と実行したところ、StackOverflowErrorが発生した。
どうやら、User→Phone→User→Phone→・・・と循環参照してしまっているらしい。。
で、次のようにPhoneからのUser参照を無くせば、エラーは出なくなった。
User.java
@Entity
public class User {
@Id
private String userId;
private String name;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "userId")
private List<Phone> services;
}
Phone.java
@Entity
public class Phone {
@Id
private Integer phoneId;
private String userId;
private Integer type;
private String number;
}
public interface UserRepository extends CrudRepository<User, Integer>, JpaSpecificationExecutor<User> {
static Specification<User> existsPhone() {
return (root, query, cb) -> {
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<Phone> subRoot = subquery.from(Phone.class);
return cb.exists(subquery.select(cb.literal(1)).where(
cb.equal(root, subRoot.get("userId")));
};
}
}
PhoneからUserを参照することもないので、これでいいっか。