Création de pages maître-détail pour les entités, comment les lier et quel bean scope choisir

j'ai commencé à apprendre le JSF, mais malheureusement la plupart des tutoriels là-bas ne présentent qu'une connexion ou une section de registre.

pouvez-vous me donner quelques exemples plus détaillés? Une chose qui m'intéresse est une page présentant une liste de produits . Je suis sur la page accueil et j'appuie sur la page produits pour que je puisse voir la dernière produits ajouté. Et à chaque fois que je visite la page, la liste des produits sera créée à partir des dernières entrées de la base de données. Comment puis-je gérer cela?

une façon de résoudre cela serait de créer une session scoped géré bean dans lequel je placerais différentes entités mis à jour par d'autres haricots gérés. J'ai trouvé ce type d'approche dans certains tutoriels, mais il semble assez difficile et maladroit.

quelle serait la meilleure approche pour résoudre une chose pareille? Quelle est l'utilisation correcte de la portée de session dans interface utilisateur master-detail de deux pages?

23
demandé sur BalusC 2011-12-11 01:10:35

2 réponses

Quelle est l'utilisation correcte de la portée de la session

utilisez-le pour les données scoped de session seulement, rien d'autre. Par exemple, l'utilisateur connecté, ses paramètres, la langue choisie, et cætera.

voir aussi:


et chaque fois que je visite la page, la liste de produits sera créée à partir des dernières entrées dans la base de données. Comment puis-je gérer cela?

généralement, vous utilisez la requête ou voir la portée pour elle. Le chargement de la liste doit se faire selon la méthode @PostConstruct . Si la page ne contient pas de <h:form> , alors la portée de la requête est très bien. Une vue scoped bean se comporterait comme une requête scoped quand il n'y a pas de <h:form> de toute façon.

Tout "voir le produit" et "modifier le produit" liens/boutons qui récupérer des informations (c'est à dire la quantité) doit être tout simplement OBTENIR <h:link> / <h:button> , dans lequel vous passez l'identifiant d'entité comme un paramètre de la requête par <f:param> .

tous "supprimer le produit" et "enregistrer le produit" liens / boutons qui seront manipuler l'information (c.-à-d. non-idempotent) devrait effectuer poste par <h:commandLink> / <h:commandButton> (vous ne voulez pas qu'ils soient bookmarkable/searchbot-indexables!). Cela nécessite à son tour un <h:form> . Afin de préserver les données pour les validations et les requêtes ajax (afin que vous n'ayez pas besoin de recharger/préinitialiser l'entité sur chaque requête), le bean devrait de préférence être vu scoped.

noter que vous devriez fondamentalement avoir un haricot séparé pour chaque vue et noter également que ces haricots n'ont pas nécessairement besoin de référence l'un l'autre.

ainsi, compte tenu de ce "produit" entité:

@Entity
public class Product {

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

    // ...
}

et ce "service produit" 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));
    }

    // ...
}

vous pouvez avoir ce "voir les produits" sur /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();
    }

    // ...
}

et vous pouvez avoir cette "modifier le produit" sur /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";
    }

    // ...
}

Et ce convertisseur pour <f:viewParam> de "modifier le produit":

@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);
        }
    }

}

voir aussi:

49
répondu BalusC 2017-05-23 11:55:03

comme une petite amélioration à ce que BalusC recommandé, parfois, vous pouvez supprimer la partie required / requiredMessage de la <f:viewParam> de vos" détails "écran et à la place utiliser le rendu conditionnel de la forme d'édition (comme BalusC l'a fait) avec une condition inverse pour recommander un lien spécifique pour l'écran" Liste/maître " ou, même utiliser une viewAction qui permettrait de tester le param et forcer une redirection à cette liste.

0
répondu Eduard Korenschi 2018-05-09 22:02:14