HI我有一个包含多个集合字段(集合)的实体。所有集合都标注有:
@LazyCollection(LazyCollectionOption.EXTRA)
和
@OneToMany(cascade = CascadeType.MERGE, orphanRemoval = true, fetch = FetchType.LAZY)
当尝试使用以下JPA查询检索实体时:
select o from Organization as o left join fetch o.members left join fetch o.book left join fetch o.accounts left join fetch o.events left join fetch o.parameters where o.id=?1
它生成的SQL是所有集合的笛卡尔乘积,最终产生数百万个结果。
我想在没有集合的情况下检索实体,但遇到了无法初始化Lazy集合的异常。
在所有集合都避免笛卡尔乘积的情况下检索实体的最佳实践是什么?
最好使用Criteria
API,这样DISTINCT
就不会添加到生成的SQL中。假设您是通过唯一的id
获取的,则方法uniqueResult()
不会失败。
public Organization fetchOrganization() {
Session session = getEntityManager().unwrap(Session.class);
Criteria criteria = session.createCriteria(Organization.class, "o")
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
.createAlias("o.members", "members", JoinType.LEFT_OUTER_JOIN)
.add(Restrictions.eq("o.id", id));
return criteria.uniqueResult();
}
您只能通过ID和获得一个组织实体
Hibernate.initialize(organizationEntity) to fetch all lazy collections.
带参数的NamedQueries为我完成了任务…
当你尝试时
entityManager.find(PoDetail.class, poNumber)
您将在具有@OneToMany
的实体中声明所有列表,这些列表使用笛卡尔乘积no-of-time初始化,这也将具有重复项。当然,可以通过使用Set
来消除这些重复,但Set不会保留数据插入的顺序,并且当试图在View中显示时,我们会对行进行加扰。
我使用解决了这个问题
NamedQueries with parameters以避免获取集合的笛卡尔乘积。
这样,您的视图数据将按照持久数据插入顺序保持原样。
这是示例代码:
父实体类:(它有更多的列表字段,我在这里提到一个)
@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();
}
}