jpa - Primefaces Lazy Datatable 排序/过滤表关系 - Eclipselink

标签 jpa jsf primefaces eclipselink

我有一个 primefaces 的惰性数据表,如下所示:

<h:form id="form">
        <p:dataTable value="#{beanReceipts.lazyModel}" paginator="true" rows="10" paginatorPosition="bottom"
                     paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
                     rowsPerPageTemplate="5,10,15" var="item" emptyMessage="#{bundle['NoData']}" reflow="true"
                     rowStyleClass="centered" styleClass="centered" lazy="true" resizableColumns="true">

            <p:column style="width:24px" styleClass="centered">
                <p:rowToggler/>
            </p:column>

            <p:column headerText="#{bundle['Name']}" filterBy="#{item.userId.name}" filterMatchMode="contains"
                      styleClass="centered" sortBy="#{item.userId.name}">
                <h:outputText value="#{item.userId.name}"/>
            </p:column>

            <p:column headerText="#{bundle['Premium']}" styleClass="centered"
                      sortBy="#{item.userId.premiumExpiresAt}">
                <p:selectBooleanCheckbox value="#{item.userId.premiumExpiresAt != null}" disabled="true"/>
            </p:column>

            <p:column headerText="#{bundle['Product']}" sortBy="#{item.productId}" styleClass="centered">
                <h:outputText value="#{item.productId}"/>
            </p:column>

            <p:column headerText="#{bundle['AutoRenew']}" sortBy="#{item.autoRenew}" styleClass="centered">
                <p:selectBooleanCheckbox value="#{item.autoRenew}" disabled="true"/>
            </p:column>

            <p:column headerText="#{bundle['ExpiresAt']}" styleClass="centered">
                <h:outputText value="#{item.expiresAt}">
                    <f:convertDateTime type="date" pattern="dd/MM/yyyy"/>
                </h:outputText>
            </p:column>

            <p:column headerText="#{bundle['PurchasedAt']}" sortBy="#{item.purchasedAt}" styleClass="centered">
                <h:outputText value="#{item.purchasedAt}">
                    <f:convertDateTime type="date" pattern="dd/MM/yyyy"/>
                </h:outputText>
            </p:column>

            <p:column headerText="#{bundle['Platform']}" sortBy="#{item.platform}" styleClass="centered">
                <h:outputText value="#{item.platform}"/>
            </p:column>

            <p:rowExpansion>
                <h:panelGrid columns="2" cellspacing="3" cellpadding="3">
                    <h:outputText value="#{bundle['OrderId']}" style="font-weight: bold"/>
                    <h:outputText value="#{item.orderId}"/>

                    <h:outputText value="#{bundle['PurchaseToken']}" style="font-weight: bold"/>
                    <h:outputText value="#{item.purchaseToken}"/>

                    <h:outputText value="#{bundle['IsProcessed']}" style="font-weight: bold"/>
                    <p:selectBooleanCheckbox value="#{item.purchasedAt != null}" disabled="true"/>

                    <h:outputText value="#{bundle['RawData']}" style="font-weight: bold"/>
                    <h:outputText value="#{item.rawData}"/>
                </h:panelGrid>

                <br/>
                <p:commandButton actionListener="#{beanReceipts.checkPaymentForUser}" icon="ui-icon-refresh"
                                 value="#{bundle['Validate']}"
                                 oncomplete="window.location.reload();" onstart="PF('loadingDia').show();"
                                 onsuccess="PF('loadingDia').hide();" style="margin: auto;display: block;"/>
            </p:rowExpansion>

            <p:column headerText="#{bundle['Options']}" styleClass="centered">
                <p:commandButton icon="ui-icon-pencil" onsuccess="PF('editdia').show();" update=":editdia"
                                 style="margin: 8px;">
                    <f:setPropertyActionListener value="#{item}" target="#{beanReceipts.editableItem}"/>
                </p:commandButton>
                <p:commandButton icon="ui-icon-trash" oncomplete="window.location.reload();"
                                 action="#{beanReceipts.deleteEntity}"
                                 onclick="if (!confirm('#{bundle['RemovePrompt']}'))return false;"
                                 style="margin: 0 auto; text-align: center;">
                    <f:setPropertyActionListener value="#{item}" target="#{beanReceipts.removableItem}"/>
                </p:commandButton>
            </p:column>

        </p:dataTable>
    </h:form>

这是我的实体类:

    public class UsersReceipts extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @Basic(optional = false)
    @Column(name = "RECEIPT_TYPE", nullable = false)
    @Enumerated(EnumType.STRING)
    private DBUserReceiptTypeEnum receiptType;

    @Column(name = "AUTO_RENEW")
    private Boolean autoRenew;

    @Column(name = "ORDER_ID", length = 255)
    private String orderId;

    @Column(name = "PRODUCT_ID", length = 255)
    private String productId;

    @Column(name = "PACKAGE_NAME", length = 255)
    private String packageName;

    @Column(name = "EXPIRES_AT")
    @Temporal(TemporalType.TIMESTAMP)
    private Date expiresAt;

    @Column(name = "PURCHASED_AT")
    @Temporal(TemporalType.TIMESTAMP)
    private Date purchasedAt;

    @Lob
    @Column(name = "PURCHASE_TOKEN", length = 65535)
    private String purchaseToken;

    @Basic(optional = false)
    @Column(name = "PLATFORM", nullable = false, length = 8)
    @Enumerated(EnumType.STRING)
    private DBAppPlatformTypeEnum platform;

    @Basic(optional = false)
    @Lob
    @Column(name = "RAW_DATA", nullable = false, length = 65535)
    private String rawData;

    @JoinColumn(name = "USER_ID", referencedColumnName = "ID", nullable = false)
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    private Users userId;

    public UsersReceipts() {
    }

    public UsersReceipts(Integer id) {
        this.id = id;
    }

    public UsersReceipts(Integer id, DBEntryStatusTypeEnum status, Date createdAt, DBAppPlatformTypeEnum platform,
                         DBUserReceiptTypeEnum receiptType, String rawData) {
        this.id = id;
        this.status = status;
        this.createdAt = createdAt;
        this.platform = platform;
        this.receiptType = receiptType;
        this.rawData = rawData;
    }

    public Boolean getAutoRenew() {
        return autoRenew;
    }

    public void setAutoRenew(Boolean autoRenew) {
        this.autoRenew = autoRenew;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }

    public String getPackageName() {
        return packageName;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public Date getExpiresAt() {
        return expiresAt;
    }

    public void setExpiresAt(Date expiresAt) {
        this.expiresAt = expiresAt;
    }

    public Date getPurchasedAt() {
        return purchasedAt;
    }

    public void setPurchasedAt(Date purchasedAt) {
        this.purchasedAt = purchasedAt;
    }

    public String getPurchaseToken() {
        return purchaseToken;
    }

    public void setPurchaseToken(String purchaseToken) {
        this.purchaseToken = purchaseToken;
    }

    public DBAppPlatformTypeEnum getPlatform() {
        return platform;
    }

    public void setPlatform(DBAppPlatformTypeEnum platform) {
        this.platform = platform;
    }

    public DBUserReceiptTypeEnum getReceiptType() {
        return receiptType;
    }

    public void setReceiptType(DBUserReceiptTypeEnum receiptType) {
        this.receiptType = receiptType;
    }

    public String getRawData() {
        return rawData;
    }

    public void setRawData(String rawData) {
        this.rawData = rawData;
    }

    @JsonIgnore
    @XmlTransient
    public Users getUserId() {
        return userId;
    }

    public void setUserId(Users userId) {
        this.userId = userId;
    }
}

正如您已经看到的,数据表中的每个“收据”对象都与“用户”对象(类)有关系 (ManyToOne)。在我添加时的数据表中:

#{item.userId.name}

它可以告诉我收据所属的用户名。这也适用于

#{item.userId.premiumExpiresAt}

第二列中的字段。我的问题是,当我尝试对惰性数据表中的第一列和第二列进行排序/过滤时,由于关系出现以下错误:

SEVERE [http-nio-8080-exec-5] com.sun.faces.application.view.FaceletViewHandlingStrategy.handleRenderException Error Rendering View[/panel/receipts.xhtml]
 java.lang.IllegalArgumentException: The attribute [userId.name] is not present in the managed type [EntityTypeImpl@422737256:UsersReceipts [ javaType: class entities.UsersReceipts descriptor: RelationalDescriptor(entities.UsersReceipts --> [DatabaseTable(test_esimibul.users_receipts)]), mappings: 14]].
    at org.eclipse.persistence.internal.jpa.metamodel.ManagedTypeImpl.getAttribute(ManagedTypeImpl.java:148)
    at org.eclipse.persistence.internal.jpa.querydef.FromImpl.get(FromImpl.java:312)
    at core.AbstractFacade.getFilterCondition(AbstractFacade.java:175)
    at core.AbstractFacade.count(AbstractFacade.java:131)
    at core.panel.crud.LazyCrudBean$EntityLazyModel.load(LazyCrudBean.java:44)

就异常而言,我需要将 usersId(在我的例子中是 Users 实体)包含到查询中,但我无法实现。这是我的 AbstractFacade 执行延迟加载的方法:

/**
     * Returns paginated, sorted and filtered result list.
     *
     * @param startingAt
     * @param maxPerPage
     * @param sortField
     * @param sortOrder
     * @param filters
     * @return
     */
    @Override
    public List<T> getAll(int startingAt, int maxPerPage, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
        EntityManager em = getEntityManager();
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<T> cq = cb.createQuery(type);
        Root<T> c = cq.from(type);
        cq.where(getFilterCondition(cb, c, filters));
        if (sortField != null) if (sortOrder == SortOrder.ASCENDING) cq.orderBy(cb.asc(c.get(sortField)));
        else if (sortOrder == SortOrder.DESCENDING) cq.orderBy(cb.desc(c.get(sortField)));
        List<T> results = em.createQuery(cq).setFirstResult(startingAt).setMaxResults(maxPerPage).getResultList();
        em.close();
        return results;
    }

    /**
     * Returns the count of rows for the given filtering criterias.
     *
     * @param filters
     * @return
     */
    @Override
    public int count(Map<String, Object> filters) {
        EntityManager em = getEntityManager();
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
        Root<T> c = cq.from(type);
        cq.where(getFilterCondition(cb, c, filters));
        cq.select(cb.count(c));
        int count = em.createQuery(cq).getSingleResult().intValue();
        em.close();
        return count;
    }

/**
     * Creates a dynamic filtering condition with the given params.
     *
     * @param cb
     * @param c
     * @param filters
     * @return
     */
    private Predicate getFilterCondition(CriteriaBuilder cb, Root<T> c, Map<String, Object> filters) {
        Predicate filterCondition = cb.conjunction();
        filters.put("status", DBEntryStatusTypeEnum.ACTIVE);
        for (Map.Entry<String, Object> filter : filters.entrySet())
            if (!filter.getValue().equals(""))
                filterCondition = cb.and(filterCondition, cb.like(c.get(filter.getKey()), String.format("%%%s%%", filter.getValue())));
        return filterCondition;
    }

我需要知道在带有排序/过滤功能的惰性数据表中使用它们时如何包含关系。非常感谢任何解决问题的帮助。

最佳答案

我不确定 filterBy 是否适用于惰性数据表。我有一个类似的案例,我通过使用新的 Primefaces 10 数据表语法解决了这个问题:

<p:datatable value="#{myView.lazymodel}" var="entry">
    <p:column field="entry.user.name"/>
    ....

这样,Primefaces 就可以检测关系和过滤了。

关于jpa - Primefaces Lazy Datatable 排序/过滤表关系 - Eclipselink,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49897694/

相关文章:

hibernate - 当repository.save时Spring Boot + JPA + Hibernate不提交

java - 动态属性名称搜索

JSF/PrimeFaces : programmatic <f:setPropertyActionListener> on <p:commandButton> not firing

java - 如何将 EL 2.2 与 Websphere 7.0 和 JSF 2.1 Mojjarra 结合使用

css - 将图像添加到 header - #{request.contextPath} 是否返回上下文名称/面孔而不仅仅是上下文名称/?

java - Hibernate Search and Play Framework,如何构建索引

java - 仅针对特定组件的 ajaxStatus

jsf - 进程 f :viewParam only on page load

css - 是否可以为 "each"primefaces 主题制作自定义 css?

jsf - 搜索功能没有执行任何操作