Создание страниц master-detail для сущностей, как связать их и какую область bean-компонента выбрать

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

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

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

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


person TGM    schedule 10.12.2011    source источник
comment
Хорошо спасибо. В этом случае, где я должен хранить список продуктов? Теперь у меня есть компонент-контроллер с именем Контроллер продуктов (со всеми действиями, которые вызываются для объекта продукта) и модель продукта (компонент Java с теми же атрибутами, что и соответствующая таблица базы данных). Где мне хранить атрибут productList, чтобы я мог получить к нему доступ со страницы product   -  person TGM    schedule 11.12.2011
comment
Очень нужен список продуктов только на странице product. И каждый раз, когда я захожу на страницу, список продуктов будет создаваться из последних записей в базе данных. Как я могу справиться с этим?   -  person TGM    schedule 11.12.2011
comment
Я бы хотел, чтобы было больше вопросов и ответов, подобных этому, для правильного использования поддерживающих компонентов. Большинство примеров, которые вы видите, просто неверны.   -  person anger    schedule 23.07.2013
comment
Название вопроса не соответствует вашему содержанию. Концепция master-detail полностью отличается от простой страницы со списком продуктов.   -  person Gilberto    schedule 13.03.2016


Ответы (2)


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

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

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


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

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

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

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

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

Итак, учитывая этот продукт:

@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.update(product);
        return "/products?faces-redirect=true";
    }

    // ...
}

И этот преобразователь для <f:viewParam> продукта редактирования:

@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 Общие.

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

person BalusC    schedule 11.12.2011
comment
+1 действительно хороший шаблон для редактирования основных/подробных сведений в JSF. Просто это надо где-то прописать. - person Mike Braun; 12.12.2011
comment
Спасибо! Ваш пост в блоге дал мне все ответы, которые мне были нужны. - person TGM; 12.12.2011
comment
@BalusC, как бы вы включили свое предложение использовать одну и ту же страницу для редактирования и просмотра в этом сообщении stackoverflow.com/questions/8768117/, когда этот пост имеет метод резервного компонента, который устанавливает логическое значение редактирования, но здесь вы предлагаете простую ссылку GET? Мне нравится вышеизложенное, оно простое, но я также хотел бы сказать: «Эй, у вас нет прав на редактирование» и перенаправить / перенаправить на чтение только в том случае, если кто-то угадает URL-адрес «edit.xhtml? product_id = ###». Я знаю это сообщение устарело, может быть, у вас есть какой-то обновленный способ сделать что-то сейчас - person jeff; 27.02.2014
comment
большое спасибо за этот отличный пример, вы спасли мой день :) - person kelvan; 06.01.2017
comment
Метод ProductService.save(), упомянутый в EditProduct.save(), отсутствует в классе ProductService EJB. - person MestreLion; 16.07.2020

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

person Eduard Korenschi    schedule 30.06.2016