Simple CRUD table with JSF 2.3 and PrimeFaces

Last Updated:  January 16, 2024 | Published: August 5, 2018

As my last blog post about a short intro to JavaServer Faces 2.3 got a lot of attention (my tweet got even retweeted by the Java EE Guardian's twitter page (@javaee_guardian) I want to continue to write about JSF. This time about CRUD applications with JSF and PrimeFaces.

Today I'll cover one of the most common use cases for developing web-based applications: displaying/inserting/updating/deleting data in a simple table. Doing this with a JavaScript Single Page Application would lead to writing a lot of code for validation, AJAX data transfer,  data manipulation, and correct error handling. On the server-side, you'll probably offer a REST API for this use case and use a database for persistent storage. With Java EE a lot of these tasks are already defined in a specification and we just have to make use of them.

Let's see how we can make the most of JSF, Bean Validation, JPA for this use case. For a short example, I pretend we are building a small CRM web-based application to manage our customers. I'll use Java EE 8, enhance the JSF 2.3 page with PrimeFaces 8.0,  deploy it to a Payara 5.201 as a docker container and use Payara's default in-memory database H2. The final page will look like the following:

JSF project setup using PrimeFaces

Starting with the pom.xml there are some changes compared to the JSF ‘Hello World' example:

For more convenient JSF elements I am using the PrimeFaces library and their ‘all-theme' library for changing the default theme later on. In addition, you need to explicitly declare the PrimeFaces repository to be able to get those themes. To be able to store the JPA entities in the H2 in-memory, I configured the persistence.xml in src/main/resources/META-INF like the following:

The web.xml looks like the following:

Here I am specifying the PrimeFaces bootstrap theme and the servlet mapping for the views. In this example, I'll just write one view (customers.xhtml) which should be loaded as the central welcome file.

Writing the CRUD application with JSF

The central Customer JPA entity is modeled as the following:

In this example, I am mixing both JPA annotations and bean validation annotations, which is ok for this small showcase but for bigger projects, I would suggest using some kind of transfer object which got the bean validation annotations and is mapped to the JPA entity within a service. The bean validation annotations (@NotEmpty, @Email,@Past) are quite important as they will help us later on with the validation of the user input.

These declarative annotations need no implementation from our side as the application server provides a reference implementation on runtime. Another cool feature which comes with bean validation and the PrimeFaces input component is that all the required input fields (e.g. @NotEmptyor @NotNull) are marked with a black asterisk ‘*' out of the box.

For the interaction with the EntityManager, I created a simple stateless EJB that is responsible for the CRUD (create, read, update and delete) operations. The code should be quite straightforward and I don't want to get in detail here:

The corresponding backing bean for the JSF view is also quite simple and it more or less just delegates the operations to the injected EJB.

All available customers are stored in a list within @PostConstruct. The additional field customer is required for storing the current inputs of the end-user in the Customer object. When a new customer is stored in the database in the add() method, the reference will be overridden to get empty input fields in the view.

The update() method is responsible for updating all available customers as I don't provide an update mechanism on each customer object. That's everything on the backend side for our simple use case. Let's continue with the view. I'll split the explanation of the view into two parts, one for the data table and the other for the form for creating a new customer.

Create the JSF views

The data table is wrapped into a simple HTML form to be able to fire some action within a <p:commandButton> element.

The <p:…> namespace is referencing the PrimeFaces components, which is declared at the top of the .xhtml file:

On top of the <p:dataTable> I am declaring a growl element which is PrimeFaces component for displaying messages which are published in the current FacesContext. The data table is using the list of customers of the backing bean for its data source and will display a row for every customer on the list.

In addition to just displaying the data, I added a <p:inputText> elements for attributes that should be updatable like the first name, last name and the email of the customer. The other fields are just read-only. For the conversion of the day of birth of the customer, I am using the <f:convertDateTime> helper element.

Every table row also gets an extra column with a delete command button to delete the selected row. The customer which should be deleted is passed to the backing bean's delete(Customer customer) method. At default, the PrimeFaces command buttons perform an AJAX request and you can specify the element which should be updated after the AJAX call in the update attribute of the <p:commandButton> element.

The last command button is responsible for saving the changes made to several customers and will update the growl element for the new Faces Message. The HTML part of creating a new customer is also quite simple:

For every attribute which is modifiable, I am providing an outputLabel, inputText and message component. For the day of birth, I added a simple PrimeFaces calendar component which will also provide a mask for the required date format. When the user inserts data and tries to create a new user, the input data will be validated according to the Bean Validation annotations at the Customer entity. A missing first name, a wrongly formatted email or a day of birth in the future will be detected and an error message will be displayed: Considering the use case and the targeted audience, I think this approach is a really productive way of creating web applications. With the PrimeFaces components, you get a set of ready-to-use elements and with the correct theme, they will also look really nice.

Writing this application with a SPA and a backend of your choice would take quite more time and you would produce more code. Try this example for yourself or have a look at my GitHub repository for the full codebase and a step-by-step tutorial to get this running on your machine in the README.md of the project.

There is quite more to discover with JSF 2.3 and Java EE in general (e.g. WebSockets, Internationalization, Security …), so stay tuned for the next blog posts. To start with JSF 2.3 I can recommend The Definitive Guide to JSF in Java EE 8 from Bauke Scholtz (@OmniFaces) & Arjan Tijms (@arjan_tijms).

Have fun writing CRUD applications with JSF and PrimeFaces,

Phil

  • Hi,

    i am having trouble to get the application running properly with IntelliJ. It seems that a older JSF version is used and not the 2.3 version. For example i am getting the errors that the DataType LocalDate is not supported for convertDateTime…
    Do you have any Idea whats wrong with my Intellij?

    Regards,
    Dennis

  • WELD-000049: Unable to invoke public void CrudCoffee.CustomersBacking.init() on CrudCoffee.CustomersBacking@23bb25b5 getting this error when using it

    • Hey Ryan,

      are you using the demo application as-is or did you slightly modify it? In case you modified it, it’s hard to tell where the issue is. Can you open an issue here and provide more information?

      Kind regards,
      Philip

    • Hi Putra,

      I’m not quite sure if I get your question. JSF is a server-side web technology, and PrimeFaces a component library for JSF. When it comes to creating REST APIs, you usually use the JAX-RS API.

      In case you we’re asking on how to fetch data for a JSF application from another REST API, make sure to take a look at the JAX-RS Client or the MicroProfile RestClient specification.

      If that doesn’t answer your question, please provide more insights 🙂

      Kind regards,
      Philip

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >