This samples demonstrates a CRUD implementation using various PrimeFaces components.
<div class="card crud-demo">
    <h:form id="form">
        <p:growl id="messages" showDetail="true" />
        <p:toolbar>
            <p:toolbarGroup>
                <p:commandButton value="New" icon="pi pi-plus" actionListener="#{crudView.openNew}"
                    update=":dialogs:manage-product-content" oncomplete="PF('manageProductDialog').show()"
                    styleClass="ui-button-success" style="margin-right: .5rem">
                    <p:resetInput target=":dialogs:manage-product-content" />
                </p:commandButton>
                <p:commandButton id="delete-products-button" value="#{crudView.deleteButtonMessage}"
                    icon="pi pi-trash" actionListener="#{crudView.deleteSelectedProducts}"
                    styleClass="ui-button-danger" disabled="#{!crudView.hasSelectedProducts()}" update="@this">
                    <p:confirm header="Confirmation" message="Delete the selected products?"
                        icon="pi pi-exclamation-triangle" />
                </p:commandButton>
            </p:toolbarGroup>
            <p:toolbarGroup align="right">
                <p:fileUpload mode="simple" skinSimple="true" label="Import" chooseIcon="pi pi-download" />
                <p:commandButton value="Export" icon="pi pi-upload" styleClass="ui-button-help" ajax="false">
                    <p:dataExporter type="pdf" target="dt-products" fileName="products" />
                </p:commandButton>
            </p:toolbarGroup>
        </p:toolbar>
        <p:dataTable id="dt-products" widgetVar="dtProducts" var="product" value="#{crudView.products}"
            reflow="true" styleClass="products-table" selectionMode="multiple" selection="#{crudView.selectedProducts}"
            rowKey="#{product.code}" paginator="true" rows="10" selectionRowMode="add" paginatorPosition="bottom">
            <f:facet name="header">
                <div class="products-table-header">
                    <span style="font-weight: bold">PRODUCTS</span>
                    <span class="filter-container ui-input-icon-left"> <i class="pi pi-search"></i>
                        <p:inputText id="globalFilter" onkeyup="PrimeFaces.debounce(() => PF('dtProducts').filter())" placeholder="Search" />
                    </span>
                </div>
            </f:facet>
            <p:ajax event="rowSelect" update=":form:delete-products-button" />
            <p:ajax event="rowUnselect" update=":form:delete-products-button" />
            <p:ajax event="rowSelectCheckbox" update=":form:delete-products-button" />
            <p:ajax event="rowUnselectCheckbox" update=":form:delete-products-button" />
            <p:ajax event="toggleSelect" update=":form:delete-products-button" />
            <p:column selectionBox="true" exportable="false"></p:column>
            <p:column headerText="Code" sortBy="#{product.code}" filterBy="#{product.code}">
                <h:outputText value="#{product.code}" />
            </p:column>
            <p:column headerText="Name" sortBy="#{product.name}" filterBy="#{product.name}">
                <h:outputText value="#{product.name}" />
            </p:column>
            <p:column headerText="Image" exportable="false">
                <p:graphicImage name="images/product/#{product.image}" library="demo" styleClass="product-image" />
            </p:column>
            <p:column headerText="Price" sortBy="#{product.price}">
                <h:outputText value="#{product.price}" style="font-weight: 700">
                    <f:convertNumber currencySymbol="$" type="currency" />
                </h:outputText>
            </p:column>
            <p:column headerText="Category" sortBy="#{product.category}" filterBy="#{product.category}">
                <h:outputText value="#{product.category}" />
            </p:column>
            <p:column headerText="Reviews" sortBy="#{product.rating}">
                <p:rating value="#{product.rating}" readonly="true" />
            </p:column>
            <p:column headerText="Status" sortBy="#{product.inventoryStatus}"
                filterBy="#{product.inventoryStatus}">
                <h:outputText styleClass="product-badge status-#{product.inventoryStatus.name().toLowerCase()}"
                    value="#{product.inventoryStatus.text}" />
            </p:column>
            <p:column exportable="false" ariaHeaderText="Actions">
                <p:commandButton icon="pi pi-pencil" update=":dialogs:manage-product-content"
                    oncomplete="PF('manageProductDialog').show()"
                    styleClass="edit-button rounded-button ui-button-success" process="@this">
                    <f:setPropertyActionListener value="#{product}" target="#{crudView.selectedProduct}" />
                    <p:resetInput target=":dialogs:manage-product-content" />
                </p:commandButton>
                <p:commandButton class="ui-button-warning rounded-button" icon="pi pi-trash"
                                 process="@this"
                                 oncomplete="PF('deleteProductDialog').show()">
                    <f:setPropertyActionListener value="#{product}" target="#{crudView.selectedProduct}" />
                </p:commandButton>
            </p:column>
        </p:dataTable>
    </h:form>
    <h:form id="dialogs">
        <p:dialog header="Product Details" showEffect="fade" modal="true" widgetVar="manageProductDialog"
            responsive="true">
            <p:outputPanel id="manage-product-content" class="ui-fluid">
                <p:outputPanel rendered="#{not empty crudView.selectedProduct}">
                    <p:graphicImage name="images/product/#{crudView.selectedProduct.image}" library="demo"
                        styleClass="product-image" rendered="#{not empty crudView.selectedProduct.image}" />
                    <div class="field">
                        <p:outputLabel for="name">Name</p:outputLabel>
                        <p:inputText id="name" value="#{crudView.selectedProduct.name}" required="true" />
                    </div>
                    <div class="field">
                        <p:outputLabel for="description">Description</p:outputLabel>
                        <p:inputTextarea id="description" value="#{crudView.selectedProduct.description}" />
                    </div>
                    <div class="field">
                        <p:outputLabel for="category">Category</p:outputLabel>
                        <p:selectOneRadio id="category" value="#{crudView.selectedProduct.category}"
                            layout="responsive" columns="2">
                            <f:selectItem itemLabel="Accessories" itemValue="Accessories" />
                            <f:selectItem itemLabel="Clothing" itemValue="Clothing" />
                            <f:selectItem itemLabel="Electronics" itemValue="Electronics" />
                            <f:selectItem itemLabel="Fitness" itemValue="Fitness" />
                        </p:selectOneRadio>
                    </div>
                    <div class="formgrid grid">
                        <div class="field col">
                            <p:outputLabel for="price">Price</p:outputLabel>
                            <p:inputNumber id="price" value="#{crudView.selectedProduct.price}" symbol=" USD"
                                symbolPosition="s" decimalSeparator="." thousandSeparator="," />
                        </div>
                        <div class="field col">
                            <p:outputLabel for="quantity">Quantity</p:outputLabel>
                            <p:spinner id="quantity" min="0" value="#{crudView.selectedProduct.quantity}" />
                        </div>
                    </div>
                </p:outputPanel>
            </p:outputPanel>
            <f:facet name="footer">
                <p:commandButton value="Save" icon="pi pi-check" actionListener="#{crudView.saveProduct}"
                    update="manage-product-content" process="manage-product-content @this" />
                <p:commandButton value="Cancel" icon="pi pi-times" onclick="PF('manageProductDialog').hide()"
                    class="ui-button-secondary" type="button" />
            </f:facet>
        </p:dialog>
        <p:confirmDialog widgetVar="deleteProductDialog" showEffect="fade" width="300"
            message="Delete the product?" header="Confirm" severity="warn">
            <p:commandButton value="Yes" icon="pi pi-check" actionListener="#{crudView.deleteProduct}"
                process="@this" update=":form:delete-products-button"
                oncomplete="PF('deleteProductDialog').hide()" />
            <p:commandButton value="No" type="button" styleClass="ui-button-secondary" icon="pi pi-times"
                onclick="PF('deleteProductDialog').hide()" />
        </p:confirmDialog>
        <p:confirmDialog global="true" showEffect="fade" width="300">
            <p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" icon="pi pi-check" />
            <p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no ui-button-secondary"
                icon="pi pi-times" />
        </p:confirmDialog>
    </h:form>
</div>
package org.primefaces.showcase.view.data.datatable;
import org.primefaces.PrimeFaces;
import org.primefaces.showcase.domain.Product;
import org.primefaces.showcase.service.ProductService;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named
@ViewScoped
public class CrudView implements Serializable {
    private static final long serialVersionUID = 1L;
    private List<Product> products;
    private Product selectedProduct;
    private List<Product> selectedProducts;
    @Inject
    private ProductService productService;
    @PostConstruct
    public void init() {
        this.products = this.productService.getClonedProducts(100);
        this.selectedProducts = new ArrayList<Product>();
    }
    public List<Product> getProducts() {
        return products;
    }
    public Product getSelectedProduct() {
        return selectedProduct;
    }
    public void setSelectedProduct(Product selectedProduct) {
        this.selectedProduct = selectedProduct;
    }
    public List<Product> getSelectedProducts() {
        return selectedProducts;
    }
    public void setSelectedProducts(List<Product> selectedProducts) {
        this.selectedProducts = selectedProducts;
    }
    public void openNew() {
        this.selectedProduct = new Product();
    }
    public void saveProduct() {
        if (this.selectedProduct.getCode() == null) {
            this.selectedProduct.setCode(UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9));
            this.products.add(this.selectedProduct);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Product Added"));
        }
        else {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Product Updated"));
        }
        PrimeFaces.current().executeScript("PF('manageProductDialog').hide()");
        PrimeFaces.current().ajax().update("form:messages", "form:dt-products");
    }
    public void deleteProduct() {
        this.products.remove(this.selectedProduct);
        this.selectedProducts.remove(this.selectedProduct);
        this.selectedProduct = null;
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Product Removed"));
        PrimeFaces.current().ajax().update("form:messages", "form:dt-products");
    }
    public String getDeleteButtonMessage() {
        if (hasSelectedProducts()) {
            int size = this.selectedProducts.size();
            return size > 1 ? size + " products selected" : "1 product selected";
        }
        return "Delete";
    }
    public boolean hasSelectedProducts() {
        return this.selectedProducts != null && !this.selectedProducts.isEmpty();
    }
    public void deleteSelectedProducts() {
        this.products.removeAll(this.selectedProducts);
        this.selectedProducts = null;
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Products Removed"));
        PrimeFaces.current().ajax().update("form:messages", "form:dt-products");
        PrimeFaces.current().executeScript("PF('dtProducts').clearFilters()");
    }
}
package org.primefaces.showcase.domain;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
public class Product implements Serializable {
    private int id;
    private String code;
    private String name;
    private String description;
    private String image;
    private double price;
    private String category;
    private int quantity;
    private InventoryStatus inventoryStatus;
    private int rating;
    private List<Order> orders;
    public Product() {
    }
    public Product(int id, String code, String name, String description, String image, double price, String category, int quantity,
            InventoryStatus inventoryStatus, int rating) {
        this.id = id;
        this.code = code;
        this.name = name;
        this.description = description;
        this.image = image;
        this.price = price;
        this.category = category;
        this.quantity = quantity;
        this.inventoryStatus = inventoryStatus;
        this.rating = rating;
    }
    @Override
    public Product clone() {
        return new Product(getId(), getCode(), getName(), getDescription(), getImage(), getPrice(), getCategory(), getQuantity(),
                getInventoryStatus(), getRating());
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getImage() {
        return image;
    }
    public void setImage(String image) {
        this.image = image;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public String getCategory() {
        return category;
    }
    public void setCategory(String category) {
        this.category = category;
    }
    public int getQuantity() {
        return quantity;
    }
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
    public InventoryStatus getInventoryStatus() {
        return inventoryStatus;
    }
    public void setInventoryStatus(InventoryStatus inventoryStatus) {
        this.inventoryStatus = inventoryStatus;
    }
    public int getRating() {
        return rating;
    }
    public void setRating(int rating) {
        this.rating = rating;
    }
    public List<Order> getOrders() {
        return this.orders;
    }
    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }
    @Override
    public int hashCode() {
        return Objects.hashCode(code);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Product other = (Product) obj;
        return Objects.equals(getCode(), other.getCode());
    }
}
package org.primefaces.showcase.domain;
public enum InventoryStatus {
    INSTOCK("In Stock"),
    OUTOFSTOCK("Out of Stock"),
    LOWSTOCK("Low Stock");
    private String text;
    InventoryStatus(String text) {
        this.text = text;
    }
    public String getText() {
        return text;
    }
}
package org.primefaces.showcase.domain;
public class Order implements java.io.Serializable {
    private final int number;
    private final String imagePath;
    public Order(int number, String imagePath) {
        this.number = number;
        this.imagePath = imagePath;
    }
    public int getNumber() {
        return number;
    }
    public String getImagePath() {
        return imagePath;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Order order = (Order) o;
        return number == order.number;
    }
    @Override
    public int hashCode() {
        return number;
    }
}
package org.primefaces.showcase.service;
import org.primefaces.showcase.domain.InventoryStatus;
import org.primefaces.showcase.domain.Product;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
@Named
@ApplicationScoped
public class ProductService {
    private List<Product> products;
    @PostConstruct
    public void init() {
        products = new ArrayList<>();
        products.add(new Product(1000, "f230fh0g3", "Bamboo Watch", "Product Description", "bamboo-watch.jpg", 65,
                "Accessories", 24, InventoryStatus.INSTOCK, 5));
        products.add(new Product(1001, "nvklal433", "Black Watch", "Product Description", "black-watch.jpg", 72,
                "Accessories", 61, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1002, "zz21cz3c1", "Blue Band", "Product Description", "blue-band.jpg", 79,
                "Fitness", 2, InventoryStatus.LOWSTOCK, 3));
        products.add(new Product(1003, "244wgerg2", "Blue T-Shirt", "Product Description", "blue-t-shirt.jpg", 29,
                "Clothing", 25, InventoryStatus.INSTOCK, 5));
        products.add(new Product(1004, "h456wer53", "Bracelet", "Product Description", "bracelet.jpg", 15,
                "Accessories", 73, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1005, "av2231fwg", "Brown Purse", "Product Description", "brown-purse.jpg", 120,
                "Accessories", 0, InventoryStatus.OUTOFSTOCK, 4));
        products.add(new Product(1006, "bib36pfvm", "Chakra Bracelet", "Product Description", "chakra-bracelet.jpg", 32,
                "Accessories", 5, InventoryStatus.LOWSTOCK, 3));
        products.add(new Product(1007, "mbvjkgip5", "Galaxy Earrings", "Product Description", "galaxy-earrings.jpg", 34,
                "Accessories", 23, InventoryStatus.INSTOCK, 5));
        products.add(new Product(1008, "vbb124btr", "Game Controller", "Product Description", "game-controller.jpg", 99,
                "Electronics", 2, InventoryStatus.LOWSTOCK, 4));
        products.add(new Product(1009, "cm230f032", "Gaming Set", "Product Description", "gaming-set.jpg", 299,
                "Electronics", 63, InventoryStatus.INSTOCK, 3));
        products.add(new Product(1010, "plb34234v", "Gold Phone Case", "Product Description", "gold-phone-case.jpg", 24,
                "Accessories", 0, InventoryStatus.OUTOFSTOCK, 4));
        products.add(new Product(1011, "4920nnc2d", "Green Earbuds", "Product Description", "green-earbuds.jpg", 89,
                "Electronics", 23, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1012, "250vm23cc", "Green T-Shirt", "Product Description", "green-t-shirt.jpg", 49,
                "Clothing", 74, InventoryStatus.INSTOCK, 5));
        products.add(new Product(1013, "fldsmn31b", "Grey T-Shirt", "Product Description", "grey-t-shirt.jpg", 48,
                "Clothing", 0, InventoryStatus.OUTOFSTOCK, 3));
        products.add(new Product(1014, "waas1x2as", "Headphones", "Product Description", "headphones.jpg", 175,
                "Electronics", 8, InventoryStatus.LOWSTOCK, 5));
        products.add(new Product(1015, "vb34btbg5", "Light Green T-Shirt", "Product Description", "light-green-t-shirt.jpg", 49,
                "Clothing", 34, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1016, "k8l6j58jl", "Lime Band", "Product Description", "lime-band.jpg", 79,
                "Fitness", 12, InventoryStatus.INSTOCK, 3));
        products.add(new Product(1017, "v435nn85n", "Mini Speakers", "Product Description", "mini-speakers.jpg", 85,
                "Clothing", 42, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1018, "09zx9c0zc", "Painted Phone Case", "Product Description", "painted-phone-case.jpg", 56,
                "Accessories", 41, InventoryStatus.INSTOCK, 5));
        products.add(new Product(1019, "mnb5mb2m5", "Pink Band", "Product Description", "pink-band.jpg", 79,
                "Fitness", 63, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1020, "r23fwf2w3", "Pink Purse", "Product Description", "pink-purse.jpg", 110,
                "Accessories", 0, InventoryStatus.OUTOFSTOCK, 4));
        products.add(new Product(1021, "pxpzczo23", "Purple Band", "Product Description", "purple-band.jpg", 79,
                "Fitness", 6, InventoryStatus.LOWSTOCK, 3));
        products.add(new Product(1022, "2c42cb5cb", "Purple Gemstone Necklace", "Product Description", "purple-gemstone-necklace.jpg", 45,
                "Accessories", 62, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1023, "5k43kkk23", "Purple T-Shirt", "Product Description", "purple-t-shirt.jpg", 49,
                "Clothing", 2, InventoryStatus.LOWSTOCK, 5));
        products.add(new Product(1024, "lm2tny2k4", "Shoes", "Product Description", "shoes.jpg", 64,
                "Clothing", 0, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1025, "nbm5mv45n", "Sneakers", "Product Description", "sneakers.jpg", 78,
                "Clothing", 52, InventoryStatus.INSTOCK, 4));
        products.add(new Product(1026, "zx23zc42c", "Teal T-Shirt", "Product Description", "teal-t-shirt.jpg", 49,
                "Clothing", 3, InventoryStatus.LOWSTOCK, 3));
        products.add(new Product(1027, "acvx872gc", "Yellow Earbuds", "Product Description", "yellow-earbuds.jpg", 89,
                "Electronics", 35, InventoryStatus.INSTOCK, 3));
        products.add(new Product(1028, "tx125ck42", "Yoga Mat", "Product Description", "yoga-mat.jpg", 20,
                "Fitness", 15, InventoryStatus.INSTOCK, 5));
        products.add(new Product(1029, "gwuby345v", "Yoga Set", "Product Description", "yoga-set.jpg", 20,
                "Fitness", 25, InventoryStatus.INSTOCK, 8));
    }
    public List<Product> getProducts() {
        return new ArrayList<>(products);
    }
    public List<Product> getProducts(int size) {
        if (size > products.size()) {
            Random rand = new Random();
            List<Product> randomList = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                int randomIndex = rand.nextInt(products.size());
                randomList.add(products.get(randomIndex));
            }
            return randomList;
        }
        else {
            return new ArrayList<>(products.subList(0, size));
        }
    }
    public List<Product> getClonedProducts(int size) {
        List<Product> results = new ArrayList<>();
        List<Product> originals = getProducts(size);
        for (Product original : originals) {
            results.add(original.clone());
        }
        // make sure to have unique codes
        for (Product product : results) {
            product.setCode(UUID.randomUUID().toString().replace("-", "").substring(0, 8));
        }
        return results;
    }
}