当一个实体的字段部分是集合时,人们希望以尽可能少的查询次数并使用尽可能少的内存来获取数据。
第一个问题通过JPQL查询中的“join fetch”解决(解决N+1问题)。
然而,“join fetch”(通过检查相应的 SQL 查询很容易看出)会导致笛卡尔积问题:与没有重数的实体字段对应的每个“行”都出现在返回的结果集中,重数为 N_1 x N_2 x 。 .. x N_m,其中 N_1 是第一个集合的重数,N_2 是第二个集合的重数,N_m 是第 m 个集合的重数,假设实体有 m 个字段,这些字段是集合。
Hibernate 使用 FetchMode.SUBSELECT 解决了这个问题(如果我没记错的话,它会进行 m+1 个查询,每个查询都不会返回冗余数据)。在 JPA 中解决此问题的标准方法是什么(在我看来,至少在这种情况下,我不能将 JPA 注释与 Hibernate 的注释混合)?
最好的方法是用查询替换集合,特别是当预期大小足够大而导致性能问题时:
您删除了双向
@OneToMany
侧,只留下拥有的 @ManyToOne
侧
您选择父实体(例如
Country
)运行如下查询:
select c from City c where c.country = :country
select c from County c where c.country = :country
select count(p), c.name from People p join p.country group by c.name
我尝试添加 @fetch(FetchMode.SUBSELECT) 或 @fetch(FetchMode.SELECT) 它不会做出任何更改,即(仍然进行连接而不是进行子选择两个查询,它会在同一查询中进行所有选择)
当你尝试使用
entityManager.find(PoDetail.class, poNumber)
您将拥有在具有
@OneToMany
的实体中声明的所有列表,并使用笛卡尔积初始化次数,其中也会有重复项。当然,可以使用 Set
消除这些重复项,但是 Set 不会保留数据插入的顺序,当尝试在 View 中显示时,我们会出现乱序的行。
我通过使用解决了这个问题:
带有参数的NamedQueries以避免获取集合的笛卡尔积。
这样做,您的视图数据将与持久数据插入顺序保持一致。
这是示例代码:
父实体类:(它有更多的列表字段,我在这里提到一个)
@Entity
@Table(name="PO_DETAILS")
@NamedQuery(name="PoDetail.findByPoNumber", query="SELECT p FROM PoDetail p where p.poNumber=:poNumber")
public class PoDetail implements Serializable {
@Id
@Column(name="PO_NUMBER", unique=true, nullable=false, length=30)
private String poNumber;
@Column(name="ACTION_TAKEN", length=2000)
private String actionTaken;
.....
//bi-directional one-to-many association to PcrDetail
@OneToMany(mappedBy="poDetail", cascade={CascadeType.ALL}, fetch=FetchType.EAGER, orphanRemoval=true)
private List<PcrDetail> pcrDetails;
子实体类:
@Entity
@Table(name="PCR_DETAILS")
public class PcrDetail implements Serializable {
@Id
@Column(name="PCR_NUMBER", unique=true, nullable=false, length=30)
private String pcrNumber;
@Column(name="CONTRACT_ID", length=30)
private String contractId;
.....
//bi-directional many-to-one association to PoDetail
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="PARENT_PO_NUMBER", insertable=false, updatable=false)
private PoDetail poDetail;
JPA DAO 类:
public PoBean getPoDetails(PoBean poBean) {
PoDetail poDetail = poBean.getPoDetail();
String poNumber = poDetail.getPoNumber();
entityManagerFactory = JpaUtil.getEntityManagerFactory();
entityManager = entityManagerFactory.createEntityManager();
try {
try {
poDetail = (PoDetail) entityManager
.createNamedQuery("PoDetail.findByPoNumber")
.setParameter("poNumber", poNumber).getSingleResult();
} catch (NoResultException nre) {
poBean.setStatusCode(PopVO.ERROR);
poBean.setErrorMessage("No PO details foun with PO Number : "
+ poNumber);
}
return poBean;
} catch (Exception e) {
e.printStackTrace();
poBean.setStatusCode(PopVO.ERROR);
poBean.setErrorMessage(e.getMessage());
return poBean;
} finally {
entityManager.close();
}
}