JPA:如何定义没有N + 1问题的复杂计数

时间:2017-06-20 01:21:20

标签: hibernate jpa

我需要帮助在JPA中生成查询。我们说我有这些表

create table PERSON (
    person_id number(3,0) not null primary key,
    last_name varchar2(15) not null,
    first_name varchar2(15) not null,
    department varchar2(15) not null
);

create table PHONE (
    phone_id number(3,0) not null primary key,
    person_id number(3,0) not null,
    phone_type varchar2(10) not null,
    CONSTRAINT fk_person_id FOREIGN KEY (person_id)
    REFERENCES person (person_id)
);

我想定义一个类似的查询:

select p1.person_id, p1.last_name, p1.first_name,
       (select count(1) from phone p2
        where p2.person_id = p1.person_id) as phone_count
from person p1;

我的JPA课程定义如下:

@Entity
@Table(name="PERSON")
@Transactional(readOnly=true)
public class Person {
    @Id
    @Column(name = "PERSON_ID")
    private Integer personId;

    @Column(name = "LAST_NAME")
    private String lastName;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "DEPARTMENT")
    private String department;

    @OneToMany
    @LazyCollection(LazyCollectionOption.FALSE)
    @Fetch(FetchMode.SELECT) // or @Fetch(FetchMode.JOIN) 
    @JoinColumn(name = "PERSON_ID", referencedColumnName="PERSON_ID")
    private Set<Phone> phones;

    .... getters and setters...
}


@Entity
@Table(name="PHONE")
@Transactional(readOnly=true)
public class Person {
    @Id
    @Column(name = "PHONE_ID")
    private Integer phoneId;

    @Column(name = "PERSON_ID")
    private Integer personId;

    @Column(name = "PHONE_TYPE")
    private String phoneType;

    .... getters and setters ...
}

由于我有动态where子句,即last_name,first_name,因此可以根据需要传入department组合。我有一个功能:

personRepository.findAll(spec, pageable);

帮助我解决动态where子句,然后通过getPhones.size()获取手机数量。它按预期工作。但是,它会产生N + 1问题。如果我有500多人,它会循环每个人的记录,并在电话桌上调用选择查询。我的问题是在这种情况下如何避免N + 1问题。

我正在考虑使用本机查询,而且我不知道如何使动态where子句工作(我的真实程序有超过15列要过滤)。感谢帮助。

1 个答案:

答案 0 :(得分:0)

等效的JPA查询将是:

SELECT
  p.personId, p.lastName, p.firstName,
  (SELECT COUNT(ph) FROM p.phones ph)
WHERE ...

返回元组,即将其用作:

TypedQuery<Object[]> q = em.createQuery(_QUERY_FROM_ABOVE_, Object[].class);
List<Object[]> results = q.getResultList();
// results.get(0)[0] -> Integer personId
// results.get(0)[1] -> String lastName
// results.get(0)[2] -> String firstName
// results.get(0)[3] -> Long COUNT

您甚至可以询问整个Person及其手机数量:

SELECT p, (SELECT COUNT(ph) FROM p.phones ph)
WHERE ...

使用与上述代码类似的代码,results.get(0)[0]是整个Person对象,results.get(0)[1]的计数为Long

相关问题