Бизнес-логика внутри метода доступа/геттера в JSF

У меня есть три таблицы в базе данных MySQL.

  • категория (исключена из этого вопроса)
  • подкатегория
  • товар

Связь между этими таблицами интуитивно понятна — один ко многим в том порядке, в котором они появляются.


Я повторяю список SubCategory, List<SubCategory>, используя <p:dataGrid> следующим образом.

<p:dataGrid var="row" value="#{featuredProductManagedBean}" rows="4" first="0" columns="1" rowIndexVar="rowIndex" paginator="true" paginatorAlwaysVisible="false" pageLinks="10" lazy="true" rowsPerPageTemplate="5,10,15">
    <h:panelGrid columns="1" style="width:100%;">
        <p:carousel value="#{featuredProductManagedBean.getProducts(row.subCatId)}" var="prodRow" numVisible="4" pageLinks="5" headerText="#{row.subCatName}" style="text-align: left;">

            <!--Display product image in <p:graphicImage>-->

        </p:carousel>
        <h:outputLink rendered="#{featuredProductManagedBean.count gt 4}" value="xxx">View More</h:outputLink>
    </h:panelGrid>

    <p:ajax event="page" onstart="PF('blockDataPanelUIWidget').block()" oncomplete="PF('blockDataPanelUIWidget').unblock()"/>
</p:dataGrid>

Существует параметризованный метод получения, связанный с атрибутом значения </p:carousel> featuredProductManagedBean.getProducts(row.subCatId).

Метод вызывается несколько раз, что приводит к тому, что дорогостоящие бизнес-службы вызываются несколько раз.

Управляемый компонент:

@ManagedBean
@ViewScoped
public final class FeaturedProductManagedBean extends LazyDataModel<SubCategory> implements Serializable
{
    @EJB
    private final FeaturedProductBeanLocal service=null;
    private Product selectedProduct;  //Getter and setter.
    private Long count;               //Getter and setter.  
    private static final long serialVersionUID = 1L;

    public FeaturedProductManagedBean() {}

    public List<Product>getProducts(Long subCatId)
    {
        List<Product>products=null;

        if(FacesContext.getCurrentInstance().getCurrentPhaseId().getOrdinal()==6)
        {
            setCount(service.countProducts(subCatId));
            products=service.getProductList(subCatId);
        }

        return products;
    }

    @Override
    public List<SubCategory> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters)
    {
        int rowCount = service.rowCount().intValue();
        setRowCount(rowCount);

        if(pageSize<=0)
        {
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_FATAL, Utility.getMessage("faces.message.error"), Utility.getMessage("pageSize.error.message"));
            FacesContext.getCurrentInstance().addMessage(null, message);
            return Collections.emptyList();
        }
        else if(first>=pageSize&&rowCount<=Utility.currentPage(first, pageSize)*pageSize-pageSize) {
            first-=pageSize;
        }

        setPageSize(pageSize);
        return service.getList(first, pageSize);
    }
}

Здесь нельзя использовать ни @PostConstruct, ни ленивую загрузку. В настоящее время я поставил условную проверку if(FacesContext.getCurrentInstance().getCurrentPhaseId().getOrdinal()==6), чтобы предотвратить многократный вызов методов службы. Правильно ли работает эта условная проверка? Есть ли у него побочные эффекты?

Этот ответ поддерживает Map, но поддержка Map для больших источников данных обходится дорого.

Есть ли точный способ предотвратить многократное выполнение такой бизнес-логики?


person Tiny    schedule 01.04.2014    source источник
comment
Чтобы бизнес-логика не выполнялась несколько раз, вы должны переместить ее из геттера и поместить в метод PostConstruct. Непонятно, почему вы говорите, что не можете.   -  person perissf    schedule 02.04.2014
comment
возможный дубликат метода вызова Primefaces dataTable несколько раз при нажатии commandButton . почему?   -  person Danubian Sailor    schedule 02.04.2014
comment
Поскольку это предположительно невозможно с использованием criteria/JPQL, @perissf   -  person Tiny    schedule 02.04.2014
comment
Если запрос не выполним с JPA, есть много обходных путей, более простых, чем то, что вы пытаетесь сделать: изменение схемы БД, разделение запроса на стволы и обработка стволов в java и т. д. Если бы я был вами, я бы избегал если возможно увеличить сложность приложения только по этой причине. Но, очевидно, я не знаю всех бизнес-требований вашего приложения...   -  person perissf    schedule 02.04.2014
comment
Это всего лишь попытка разбить запрос на части, как вы сказали, @perissf   -  person Tiny    schedule 02.04.2014


Ответы (1)


Я предлагаю хранить продукты загруженного в данный момент subCategory в Map сразу после того, как они будут загружены ленивой моделью <p:dataGrid>. Карта никогда не вырастет слишком сильно, потому что вы будете очищать ее при каждом вызове метода load:

@ManagedBean
@ViewScoped
public final class FeaturedProductManagedBean extends LazyDataModel<SubCategory> implements Serializable
{
    @EJB
    private final FeaturedProductBeanLocal service=null;
    private Product selectedProduct;  //Getter and setter.
    private Long count;               //Getter and setter.  
    private Map<Long, Product> productsBySubCategory = new HashMap<>(); //Getter only.
    private static final long serialVersionUID = 1L;

    @Override
    public List<SubCategory> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters)
    {
        int rowCount = service.rowCount().intValue();
        setRowCount(rowCount);

        List<SubCategory> subCategories = service.getList(first, pageSize);
        productsBySubCategory.clear();
        for (SubCategory sc : subCategories) 
        {
            productsBySubCategory.put(sc.getSubCatId(), service.getProductList(sc.getSubCatId());
        }
        return subCategories;
    }
}

И получить прямой доступ к Map из EL:

<p:carousel value="#{featuredProductManagedBean.productsBySubCategory[row.subCatId]}" />

Обратите внимание, что вам не нужен весь этот шаблон, который проверяет границы страницы и устанавливает размер страницы.

person perissf    schedule 02.04.2014
comment
Сделанный! Спасибо большое :) - person Tiny; 02.04.2014