JPA检索集合,避免笛卡尔乘积



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();
    }
}

最新更新