我正在使用JPA存储库在我的Web应用程序中实现查询。我要查询的两个主要表格是FmReportTb
和SpecimenTb
。
以下是两个实体类(仅列出重要属性)。
//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;
FmReportTb
与SpecimenTb
有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
的{{1}}加mrn
的一些属性。
这是SpecimenTb
的JPA存储库:
FmReportTb
因为,我只显示@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
创建一个值对象。 VO类的构造函数根据OneToOne关系从FmReportTb
分配属性并从FmReportTb
获取mrn
属性。使用VO的另一个原因是因为表SpecimenTb
有很多OneToMany子实体。对于这个特定的查询,我不需要任何查询。
FmReportTb
我在servicebean类中实现了findall方法,以返回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();
}
VO的列表。
FmReportTb
最后,我的控制器看起来像这样:
//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;
}
数据库中有大约200条记录。令人惊讶的是,用JSON检索所有记录花了差不多2秒钟。即使我没有索引所有表,但这太慢了。类似的查询直接在数据库上花费大约几毫秒。是因为我使用的是Value Objects还是JPA查询往往会这么慢?
编辑1
这可能与FmReportTb具有近20个OneToMany实体的事实有关。虽然这些子实体的fetchmode设置为LAZY,JPA Data repository tends to ignore the fetchmode。所以我最终使用@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);
}
来指定EAGER属性。下一节将添加到我的FmReportTb实体类的头部。
NamedEntityGraph
然后在JPA存储库查询之前添加@Entity
@NamedEntityGraph(
name = "FmReportGraph",
attributeNodes = {
@NamedAttributeNode("fileLoadId"),
@NamedAttributeNode("frBlockId"),
@NamedAttributeNode("frCollectionDate"),
@NamedAttributeNode("frDiagnosis"),
@NamedAttributeNode("frFullName"),
@NamedAttributeNode("frReportId"),
@NamedAttributeNode("specimenTb")})
@Table(name="FM_REPORT_TB")
以查找所有记录。在这之后,性能得到了一点改善。现在获取1500条记录只需要大约10秒钟。但是,鉴于每个json对象相当小,它看起来仍然太慢。
答案 0 :(得分:0)
JPA查询速度缓慢的其他人正在寻求帮助...
正如@Ken Bekov在评论中暗示的那样,外键对JPA可以有很大帮助。
我有几个具有多对一关系的表-100,000条记录的查询要花几个小时才能执行。无需任何代码更改,我只需添加外键即可将其减少到几秒钟。
在phpMyAdmin中,您可以通过创建从“许多”表到“一个”表的关系来做到这一点。有关详细说明,请参见以下问题:Setting up foreign keys in phpMyAdmin? 还有@Devsi Odedra的答案