Создание страниц главной страницы для объектов, как их связать и в какой области bean выбрать

Я начал изучать JSF, но, к сожалению, большинство учебников там содержат только журнал или раздел реестра.

Можете ли вы указать мне более подробные примеры? Меня интересует страница, представляющая список продуктов. Я нахожусь на домашней странице, и я нажимаю на страницы, чтобы я мог видеть последние добавленные продукты. И каждый раз, когда я посещаю страницу, список продуктов будет создан из последних записей в базе данных. Как я могу справиться с этим?

Одним из способов решения этой задачи было бы создать управляемый сеансом управляемый bean, в котором я бы поместил разные сущности, обновленные через другие управляемые beans. Я нашел такой подход в некоторых учебниках, но это кажется довольно сложным и неуклюжим.

Какой был бы лучший подход к решению такой вещи? Какое правильное использование области сеанса в двухстраничном пользовательском интерфейсе master-detail?

Ответ 1

Как правильно использовать область сеанса

Используйте его только для данных сессий, ничего больше. Например, вошедший в систему пользователь, его настройки, выбранный язык и так далее.

Смотрите также:


И каждый раз, когда я захожу на страницу, список товаров будет создаваться из последних записей в базе данных. Как я могу справиться с этим?

Обычно вы используете запрос или просматриваете область действия для него. Загрузка списка должна происходить в методе @PostConstruct. Если страница не содержит <h:form>, тогда область запроса в порядке. Бин с областью представления будет вести себя как запрос, если в любом случае <h:form>.

Все ссылки/кнопки "Просмотр продукта" и "Редактирование продукта", которые просто извлекают информацию (т.е. идемпотент), должны быть просто GET <h:link>/<h:button> где вы передаете идентификатор объекта в качестве параметра запроса с помощью <f:param>.

Все ссылки/кнопки "удалить продукт" и "сохранить продукт", которые будут манипулировать информацией (т.е. не идемпотентными), должны выполнять POST с помощью <h:commandLink>/<h:commandButton> (вы не хотите, чтобы они были закладками /searchbot -indexable!). Это в свою очередь требует <h:form>. Чтобы сохранить данные для проверок и запросов ajax (чтобы вам не нужно было перезагружать/предварительно инициализировать сущность при каждом запросе), боб предпочтительно должен иметь область видимости.

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

Итак, учитывая этот "продукт" сущность:

@Entity
public class Product {

    @Id
    private Long id;
    private String name;
    private String description;

    // ...
}

И этот "продукт обслуживания" EJB:

@Stateless
public class ProductService {

    @PersistenceContext
    private EntityManager em;

    public Product find(Long id) {
        return em.find(Product.class, id);
    }

    public List<Product> list() {
        return em.createQuery("SELECT p FROM Product p", Product.class).getResultList();
    }

    public void create(Product product) {
        em.persist(product);
    }

    public void update(Product product) {
        em.merge(product);
    }

    public void delete(Product product) {
        em.remove(em.contains(product) ? product : em.merge(product));
    }

    // ...
}

Вы можете иметь это "просмотреть продукты" на /products.xhtml:

<h:dataTable value="#{viewProducts.products}" var="product">
    <h:column>#{product.id}</h:column>
    <h:column>#{product.name}</h:column>
    <h:column>#{product.description}</h:column>
    <h:column>
        <h:link value="Edit" outcome="/products/edit">
            <f:param name="id" value="#{product.id}" />
        </h:link>
    </h:column>
</h:dataTable>
@Named
@RequestScoped
public class ViewProducts {

    private List<Product> products; // +getter

    @EJB
    private ProductService productService;

    @PostConstruct
    public void init() {
        products = productService.list();
    }

    // ...
}

И у вас может быть этот "продукт редактирования" на /products/edit.xhtml:

<f:metadata>
    <f:viewParam name="id" value="#{editProduct.product}" 
        converter="#{productConverter}" converterMessage="Unknown product, please use a link from within the system."
        required="true" requiredMessage="Bad request, please use a link from within the system."
    />
</f:metadata>

<h:messages />

<h:form rendered="#{not empty editProduct.product}>
    <h:inputText value="#{editProduct.product.name}" />
    <h:inputTextarea value="#{editProduct.product.description}" />
    ...
    <h:commandButton value="save" action="#{editProduct.save}" />
</h:form>
@Named
@ViewScoped
public class EditProduct {

    private Product product; // +getter +setter

    @EJB
    private ProductService productService;

    public String save() {
        productService.save(product);
        return "/products?faces-redirect=true";
    }

    // ...
}

И этот конвертер для <f:viewParam> из "edit product":

@Named
@RequestScoped
public class ProductConverter implements Converter {

    @EJB
    private ProductService productService;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }

        try {
            Long id = Long.valueOf(value);
            return productService.find(id);
        } catch (NumberFormatException e) {
            throw new ConverterException("The value is not a valid Product ID: " + value, e);
        }
    }

    @Override    
    public String getAsString(FacesContext context, UIComponent component, Object value) {        
        if (value == null) {
            return "";
        }

        if (value instanceof Product) {
            Long id = ((Product) value).getId();
            return (id != null) ? String.valueOf(id) : null;
        } else {
            throw new ConverterException("The value is not a valid Product instance: " + value);
        }
    }

}

Вы даже можете использовать универсальный конвертер, это объясняется в Конвертерах реализации для сущностей с Java Generics.

Смотрите также:

Ответ 2

В качестве небольшого улучшения по сравнению с тем, что рекомендовано BalusC, иногда вы можете удалить часть required/requiredMessage из <f:viewParam> экрана "Детали" и вместо этого использовать условную визуализацию формы редактирования (как это сделал BalusC) с обратным условием. чтобы рекомендовать конкретную ссылку для экрана "список/мастер" или даже использовать viewAction, который проверил бы параметр и принудительно перенаправил бы к этому списку.