为什么JPA查询这么慢?

问题描述 投票:1回答:1

我正在使用JPA存储库在Web应用程序中实现查询。我要查询的两个主表是FmReportTbSpecimenTb

这里是两个实体类(仅列出重要的属性)。

//FmReportTb.java
@Entity
@Table(name="FM_REPORT_TB")
public class FmReportTb implements Serializable {

    @Column(name="ROW_ID")
    private long rowId;

    @Column(name="FR_BLOCK_ID")
    private String frBlockId;

    @Column(name="FR_FULL_NAME")
    private String frFullName;
    @OneToOne
    @JoinColumn(name="SPECIMEN_ID")
    private SpecimenTb specimenTb;

[FmReportTbSpecimenTb具有OneToOne关系。

@Entity
@Table(name="SPECIMEN_TB")
public class SpecimenTb implements Serializable {
    private String mrn;
    @OneToOne(mappedBy="specimenTb", cascade=CascadeType.ALL)
    private FmReportTb fmReportTb;

我正在处理的查询是在FmReportTb中查找所有记录,并显示FmReportTb中的一些属性以及mrn中的SpecimenTb。这是我的FmReportTb的JPA存储库:

@Repository
public interface FmReportRepository extends JpaRepository<FmReportTb, Long> {
    @Query("select f from FmReportTb f where f.deleteTs is not null")
    public List<FmReportTb> findAllFmReports();

[因为只显示FmReportTb的部分属性,而SpecimenTb的一个属性,所以我决定为FmReportTb创建一个值对象。 VO类的构造函数基于OneToOne关系从FmReportTb分配属性,并从mrn获取SpecimenTb属性。使用VO的另一个原因是因为表FmReportTb具有很多OneToMany子实体。对于此特定查询,我不需要任何一个。

public class FmReportVO {
    private String frBlockId;
    private Date frCollectionDate;
    private String frCopiedPhysician;
    private String frDiagnosis;
    private String frFacilityName;
    private String frFullName;
    private String frReportId;
    private String filepath;
    private String mrn;

    public FmReportVO(FmReportTb fmReport) {
        this.frBlockId = fmReport.getFrBlockId();
        this.frCollectionDate = fmReport.getFrCollectionDate();
        this.frCopiedPhysician = fmReport.getFrCopiedPhysician();
        this.frDiagnosis = fmReport.getFrDiagnosis();
        this.frFacilityName = fmReport.getFrFacilityName();
        this.frFullName = fmReport.getFrFullName();
        this.frReportId = fmReport.getFrReportId();
        this.mrn = fmReport.getSpecimenTb().getMrn();
    }

我在servicebean类中实现了findall方法,以返回FmReportTb VO的列表。

//FmReportServiceBean.java
    @Override
    public List<FmReportVO> findAllFmReports() {
        List<FmReportTb> reports = fmReportRepository.findAllFmReports();
        if (reports == null) {
            return null;
        }
        List<FmReportVO> fmReports = new ArrayList<FmReportVO>();
        for (FmReportTb report : reports) {
            FmReportVO reportVo = new FmReportVO(report);
            String filepath = fileLoadRepository.findUriByFileLoadId(report.getFileLoadId().longValue());
            reportVo.setFilepath(filepath);
            fmReports.add(reportVo);
        }
        return fmReports;
    }

最后,我的控制器看起来像这样:

@RequestMapping(
        value = "/ristore/foundation/",
        method = RequestMethod.GET,
        produces = "application/json")
public ResponseEntity<List<FmReportVO>> getAllFmReports() {
    List<FmReportVO> reports = ristoreService.findAllFmReports();
    if (reports == null) {
        return new ResponseEntity<List<FmReportVO>>(HttpStatus.NOT_FOUND);
    }
    return new ResponseEntity<List<FmReportVO>>(reports, HttpStatus.OK);
}

数据库中大约有200条记录。令人惊讶的是,它花了将近2整秒钟来检索JSON中的所有记录。即使我没有索引所有表,这也太慢了。直接在数据库上进行类似的查询大约需要花费几毫秒。是因为我使用的是Value Objects还是JPA查询往往这么慢?

编辑1这可能与FmReportTb具有近20个OneToMany实体有关。尽管这些子实体的提取模式设置为LAZY,JPA Data repository tends to ignore the fetchmode。因此,我最终使用NamedEntityGraph指定属性EAGER。下一部分添加到我的FmReportTb实体类的头部。

@Entity
@NamedEntityGraph(
        name = "FmReportGraph", 
        attributeNodes = { 
          @NamedAttributeNode("fileLoadId"),
          @NamedAttributeNode("frBlockId"),
          @NamedAttributeNode("frCollectionDate"),
          @NamedAttributeNode("frDiagnosis"),
          @NamedAttributeNode("frFullName"),
          @NamedAttributeNode("frReportId"),
          @NamedAttributeNode("specimenTb")})
@Table(name="FM_REPORT_TB")

然后在JPA存储库查询之前添加了@EntityGraph("FmReportGraph")以查找所有记录。之后,性能会有所提高。现在,获取1500条记录仅需大约10秒钟。但是,鉴于每个json对象都很小,这似乎仍然太慢。

spring hibernate rest jpa-2.0
1个回答
0
投票

为速度较慢的JPA查询的其他人谋福利...

正如@Ken Bekov在评论中暗示的那样,外键对JPA有很大帮助。

我有几个具有多对一关系的表-100,000条记录的查询要花几个小时才能执行。在不进行任何代码更改的情况下,我只需添加外键即可将其减少到几秒钟。

在phpMyAdmin中,您可以通过创建从“许多”表到“一个”表的关系来做到这一点。有关详细说明,请参见以下问题:Setting up foreign keys in phpMyAdmin?和@Devsi Odedra的回答

© www.soinside.com 2019 - 2024. All rights reserved.