Создание основных страниц для сущностей, как их связать и какую область действия bean выбрать.
Я начал изучать JSF, но, к сожалению, большинство учебных пособий содержат только вход в систему или раздел регистрации.
Можете ли вы указать мне более подробные примеры? Одна вещь, которая меня интересует, это страница со списком продуктов. Я нахожусь на домашней странице и нажимаю на страницу товаров, чтобы увидеть последние добавленные товары. И каждый раз, когда я захожу на страницу, список товаров будет создаваться из последних записей в базе данных. Как я могу справиться с этим?
Один из способов решить эту проблему - создать управляемый bean-объект сессионной области, в который я поместил бы различные сущности, обновленные с помощью других управляемых bean-компонентов. Я нашел такой подход в некоторых уроках, но он кажется довольно сложным и неуклюжим.
Какой будет лучший подход для решения такой вещи? Как правильно использовать область сеанса в двухстраничном пользовательском интерфейсе master-detail?
2 ответа
Как правильно использовать область сеанса
Используйте его только для данных сессий, ничего больше. Например, вошедший в систему пользователь, его настройки, выбранный язык и так далее.
Смотрите также:
И каждый раз, когда я захожу на страницу, список товаров будет создаваться из последних записей в базе данных. Как я могу справиться с этим?
Обычно вы используете запрос или просматриваете область действия для него. Загрузка списка должна происходить в @PostConstruct
метод. Если страница не содержит каких-либо <h:form>
, тогда объем запроса в порядке. Бин в области видимости будет вести себя как запрос, в котором нет <h:form>
тем не мение.
Все ссылки / кнопки "Просмотр продукта" и "Редактирование продукта", которые просто извлекают информацию (т.е. идемпотент), должны быть просто GET <h:link>
/ <h:button>
при этом вы передаете идентификатор объекта в качестве параметра запроса <f:param>
,
Все ссылки / кнопки "удалить продукт" и "сохранить продукт", которые будут манипулировать информацией (т.е. неидемпотентными), должны выполнять POST по <h:commandLink>
/ <h:commandButton>
(Вы не хотите, чтобы они были добавлены в закладки /searchbot-indexable!). Это в свою очередь требует <h:form>
, Чтобы сохранить данные для проверок и запросов ajax (чтобы вам не нужно было перезагружать / предварительно инициализировать сущность при каждом запросе), боб предпочтительно должен иметь область видимости.
Обратите внимание, что у вас должны быть отдельные bean-компоненты для каждого представления, а также обратите внимание, что эти 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.save(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);
}
}
}
Смотрите также:
Как небольшое улучшение к тому, что BalusC рекомендовал, иногда вы можете удалить required
/ requiredMessage
часть из <f:viewParam>
вашего экрана "детали" и вместо этого используйте условную визуализацию формы редактирования (как это сделал BalusC) с обратным условием для рекомендации конкретной ссылки для экрана "список / мастер" или даже используйте viewAction, который будет тестировать параметр и вызвать перенаправление в этот список.