当一个实体的字段部分是集合时,人们希望获取具有尽可能少的查询数据并使用尽可能少的内存的数据。
第一个问题通过JPQL查询中的“join fetch”来解决(解决N + 1问题)。
然而,“连接提取”(通过检查相应的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的注释)?
答案 0 :(得分:4)
最安全的方式是用查询替换集合,尤其是当预期的大小足以导致性能问题时:
删除双向@OneToMany端,只留下拥有@ManyToOne端
您选择父实体(例如国家/地区),然后只需运行以下查询:
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
有关使用JPA和Hibernate映射集合的最佳方法的更多详细信息,请查看this article。
答案 1 :(得分:0)
我试图添加@fetch(FetchMode.SUBSELECT)或@fetch(FetchMode.SELECT)它没有做任何改变,即(仍然进行连接而不是make subselect两个quires,它使所有选择在同一个查询中)
答案 2 :(得分:0)
当您尝试使用
时 int nextLeft=30;
foreach (Names name in allNames)
{
Button tempButton = new Button();
tempButton.Name = name._id;
tempButton.Text = name._name;
tempButton.Location = new System.Drawing.Point(name.positionX + nextLeft,name.positionY);
listView.Controls.Add(tempButton);
nextLeft+=30;
}
您将在entityManager.find(PoDetail.class, poNumber)
实体中声明的所有列表中都没有使用笛卡尔积进行初始化,这也将具有重复项。当然,可以使用@OneToMany
消除这些重复项,但Set不保留数据插入的顺序,当尝试在View中显示时,我们有乱码行。
我通过使用:
解决了这个问题带参数的NamedQueries,以避免收集笛卡尔积的产品。
这样做您的视图数据将与持久数据插入顺序一样。
以下是示例代码:
父实体类:(它有更多的列表字段,我在这里提到一个)
Set
儿童实体类:
@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;
JPA DAO课程:
@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;