Suppose you have a table with this structure.

If you want to issue a slightly complicated query using Spring Data JPA, use Specification, but if you use exists, the following Entity structure caused a problem. (Setter / getter omitted) By the way, the version of Spring Boot is 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;
}
Using these Entity, the exists clause to "get User who has at least one Phone"
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")));
        };
    }
}
Make like
userRepository.findAll(Specifications.where(UserRepository.existsPhone()));
When I executed, a StackOverflowError occurred.
Apparently, it seems that it has been circularly referenced as User → Phone → User → Phone → .... ..
So, if I remove the User reference from Phone as follows, the error disappeared.
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")));
        };
    }
}
I don't even refer to User from Phone, so is this okay?
Recommended Posts