jsf - 为实体创建主从页面、如何链接它们以及选择哪个 bean 范围

标签 jsf session scope master-detail

我已经开始学习 JSF,但遗憾的是,大多数教程只提供登录或注册部分。

你能指出一些更深入的例子吗?我感兴趣的一件事是显示产品列表的页面。我在主页上,我按下页面上的产品,以便我可以看到添加的最新产品。每次我访问该页面时,都会根据数据库中的最新条目创建产品列表。我该如何处理?

解决这个问题的一种方法是创建一个 session 范围的托管 bean,我将在其中放置通过其他托管 bean 更新的不同实体。我在一些教程中发现了这种方法,但看起来相当困难和笨拙。

哪种方法是解决此类问题的最佳方法?在两页主从用户界面中 session 范围的正确用法是什么?

最佳答案

What is the correct usage of session scope


仅将其用于 session 范围的数据,仅此而已。例如,登录用户、其设置、所选语言等。
也可以看看:
  • How to choose the right bean scope?

  • And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?


    通常,您使用它的请求或 View 范围。列表的加载应该发生在 @PostConstruct方法。如果页面不包含任何 <h:form> ,那么请求范围就可以了。当没有 <h:form> 时, View 作用域 bean 的行为就像一个作用域请求。反正。
    所有仅检索信息(即幂等)的“查看产品”和“编辑产品”链接/按钮应该只是简单的 GET <h:link>/<h:button>其中您通过 <f:param> 将实体标识符作为请求参数传递.
    所有将操作信息(即非幂等)的“删除产品”和“保存产品”链接/按钮都应通过 <h:commandLink> 执行 POST/<h:commandButton> (您不希望它们可添加书签/可搜索机器人索引!)。这反过来又需要一个 <h:form> .为了保留验证和 ajax 请求的数据(这样您就不需要在每个请求上重新加载/预初始化实体),bean 最好是 View 范围的。
    请注意,您基本上应该为每个 View 拥有一个单独的 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.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);
            }
        }
    
    }
    
    您甚至可以使用通用转换器,这在 Implement converters for entities with Java Generics 中有解释.
    也可以看看:
  • How to navigate in JSF? How to make URL reflect current page (and not previous one)
  • JSF Controller, Service and DAO
  • JSF Service Layer
  • How to inject @EJB, @PersistenceContext, @Inject, @Autowired, etc in @FacesConverter?
  • Communication in JSF 2.0 - Contains several examples/hints
  • 关于jsf - 为实体创建主从页面、如何链接它们以及选择哪个 bean 范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8459903/

    相关文章:

    java - XPages/JSF : Date value binding to managed bean reads but doesn't write

    validation - 每个 jsf 验证器标记的单独错误消息

    javascript - 如何防止 sessionStorage 在 JavaScript 控制台中显示?

    php - 我可以在打字时使用 jQuery 更新 PHP session 吗?

    python - sqlalchemy:使用装饰器为多个函数提供线程安全 session

    Javascript函数从另一个函数记录到控制台

    javascript - 如何使用 JavaScript 获取引用元素?

    java - 从支持 bean 访问 JSF 组件标记属性值

    c - 为什么不需要释放静态数组?

    javascript - AngularJS ng-repeat ng-click