Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / Java

A Simple CRUD Example with JSF

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
16 Sep 2015CPOL3 min read 120.6K   3.6K   4   4
This is a simple CRUD example with JSF.

Introduction

This is a simple CRUD example with JSF.

Background

JSF is an MVC framework, but it is very different from the Spring MVC and ASP.NET MVC. It actually has a strong ASP.NET Web Form "POSTBACK" flavor. This example is based on a stack overflow example.

The attached is a Maven project. I have tested it with Maven 3.2.1, Java 1.8.0_45, Tomcat 7, and Eclipse Java EE IDE for Web Developers Luna Service Release 2. It actually has two examples.

  • The "simplecrud.xhtml" is the simple CRUD example;
  • The "freshsafecrud.xhtml" is to address the problems due to the JSF "POSTBACK" nature.

The "welcome.xhtml" is the index page of the two examples.

If you are not familiar with how to import Maven projects into Eclipse, you can check out this link. When I tried to import the project, I noticed that Eclipse showed me some error message telling me some validation errors. You can simply ignore these messages and delete the markers from Eclipse.

The pom.xml and the web.xml

The "pom.xml" declared the dependencies needed to create a JSF application.

XML
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
    http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.song.example</groupId>
    <artifactId>JSF-Example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    
    <properties>
        <tomcat.version>7.0.55</tomcat.version>
        <jsf.version>2.1.7</jsf.version>
    </properties>
          
    <dependencies>         
        <!-- Sevlet jars for compilation, provided by Tomcat -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>${tomcat.version}</version>
            <scope>provided</scope>
        </dependency>
    
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>${jsf.version}</version>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>${jsf.version}</version>
        </dependency>
    
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
    
    </dependencies>
      
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                <source>1.8</source>
                <target>1.8</target>
                </configuration>
            </plugin>
                      
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                <warSourceDirectory>WebContent</warSourceDirectory>
                <failOnMissingWebXml>true</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
</project>

The "web.xml" of the example application is the following.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    
    <display-name>JSF Example</display-name>
    <welcome-file-list>
        <welcome-file>welcome.xhtml</welcome-file>
    </welcome-file-list>
    
    <filter>
        <filter-name>nocachefilter</filter-name>
        <filter-class>
            com.song.web.filter.NocacheFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>nocachefilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <servlet>
        <servlet-name>jsf-servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jsf-servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
    
    <session-config>
        <session-timeout>20</session-timeout>
        <tracking-mode>COOKIE</tracking-mode>
    </session-config>
    
    <context-param>
        <param-name>BaseUrl</param-name>
        <param-value>
            http://localhost:8080/JSF-Example/
        </param-value>
    </context-param>
</web-app>
  • From a servlet container point of view, all the JSF pages will be mapped to the single servlet implemented by the "javax.faces.webapp.FacesServlet" class;
  • In this example application, we mapped all the requests to all the "xhtml" pages to the "javax.faces.webapp.FacesServlet".

The Simple CRUD Example

The manage bean of the simple example is implemented in the "SimpleCrudBean" class.

Java
package com.song.jsf.example;
    
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
    
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
    
@ManagedBean
@SessionScoped
public class SimpleCrudBean implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private List<Student> list;
    private Student item = new Student();
    private Student beforeEditItem = null;
    private boolean edit;
    
    @PostConstruct
    public void init() {
        list = new ArrayList<Student>();
    }
    
    public void add() {
        // DAO save the add
        item.setId(list.isEmpty() ? 1 : list.get(list.size() - 1).getId() + 1);
        list.add(item);
        item = new Student();
    }
    
    public void resetAdd() {
        item = new Student();
    }
    
    public void edit(Student item) {
        beforeEditItem = item.clone();
        this.item = item;
        edit = true;
    }
    
    public void cancelEdit() {
        this.item.restore(beforeEditItem);
        this.item = new Student();
        edit = false;
    }
    
    public void saveEdit() {
        // DAO save the edit
        this.item = new Student();
        edit = false;
    }
    
    public void delete(Student item) throws IOException {
        // DAO save the delete
        list.remove(item);
    }
    
    public List<Student> getList() {
        return list;
    }
    
    public Student getItem() {
        return this.item;
    }
    
    public boolean isEdit() {
        return this.edit;
    }
    
}
  • The "add", "saveEdit", and "delete" methods need to synchronize the data with a persistent storage like a database;
  • In this example, the database operation is skipped for simplicity.

The "Student" class is implemented in the "Student.java" file.

Java
package com.song.jsf.example;
    
import java.io.Serializable;
    
public class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String name;
    
    public Student() {}
    public Student(Long id, String name) {
        this.id = id;
        this.name = name;
    }
    
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    @Override
    public Student clone() {
        return new Student(id, name);
    }
    
    public void restore(Student student) {
        this.id = student.getId();
        this.name = student.getName();
    }
}

The "SimpleCrudBean" class is bound to the "simplecrud.xhtml" file to make it fully functional.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">
<head>
    <title>Simple CRUD</title>
</head>
    
<body>
    <h3>List students</h3>
    <h:form rendered="#{not empty simpleCrudBean.list}">
        <h:dataTable value="#{simpleCrudBean.list}" var="item">
            <h:column><f:facet name="header">ID</f:facet>#{item.id}</h:column>
            <h:column><f:facet name="header">Name</f:facet>#{item.name}</h:column>
            <h:column>
                <h:commandButton value="edit" action="#{simpleCrudBean.edit(item)}" />
            </h:column>
            <h:column>
                <h:commandButton value="delete" action="#{simpleCrudBean.delete(item)}" />
            </h:column>
        </h:dataTable>
    </h:form>
    
    <h:panelGroup rendered="#{empty simpleCrudBean.list}">
        <p>No students! Please add students.</p>
    </h:panelGroup>
    
    <h:panelGroup rendered="#{!simpleCrudBean.edit}">
        <h3>Add student</h3>
        <h:form>
            <p>Name: <h:inputText value="#{simpleCrudBean.item.name}" /></p>
            <p>
                <h:commandButton value="add" action="#{simpleCrudBean.add}" />
                <h:commandButton value="reset" action="#{simpleCrudBean.resetAdd}" />
            </p>
        </h:form>
    </h:panelGroup>
    
    <h:panelGroup rendered="#{simpleCrudBean.edit}">
        <h3>Edit student #{simpleCrudBean.item.id}</h3>
        <h:form>
            <p>Name: <h:inputText value="#{simpleCrudBean.item.name}" /></p>
            <p>
                <h:commandButton value="save" action="#{simpleCrudBean.saveEdit}" />
                <h:commandButton value="cancel" action="#{simpleCrudBean.cancelEdit}" />
            </p>
        </h:form>
    </h:panelGroup>
    <p>
        <a href="#{appUrlStore.baseUrl}">Go back to index</a>
    </p>
</body>
</html>

If you now load the "simplecrud.xhtml" page, you can find that the CRUD operations all work fine. But if you click the refresh button on the browser after adding a student, you will see this ugly popup message.

If you click the "Continue" button and proceed with the refresh, you will notice that the student just added gets added again. This is definitely not a good behavior. This kind of behavior is due to the JSF's "POSTBACK" nature. In the next example, we will try to resolve this issue.

The Refresh-safe CRUD Example

In order to address the problem in the simple CRUD example, I created the "CommonUtils" class.

package com.song.jsf.example.util;
    
import java.io.IOException;
import java.io.Serializable;
    
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
    
@ManagedBean(name="commonUtils")
@ApplicationScoped
public class CommonUtils implements Serializable {
    private static final long serialVersionUID = 1L;
    
    public void redirectWithGet() {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();
        HttpServletRequest request = (HttpServletRequest)externalContext.getRequest();
    
        StringBuffer requestURL = request.getRequestURL();
        String queryString = request.getQueryString();
    
        if (queryString != null) {
            requestURL.append('?').append(queryString).toString();
        }
    
        String url = requestURL.toString();
        try {
            externalContext.redirect(requestURL.toString());
        } catch (IOException e) {
            throw new RuntimeException("Unable to rerirect to " + url);
        }
    
        facesContext.responseComplete();
    }
}

The "redirectWithGet" method is to simply send a redirect request to the browser to refresh the browser with a GET request. The "CommonUtils" object is injected into the "FreshsafeCrudBean" class, and the "redirectWithGet" method is called whenever a "POSTBACK" is performed.

package com.song.jsf.example;
    
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
    
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
    
import com.song.jsf.example.util.CommonUtils;
    
@ManagedBean
@SessionScoped
public class FreshsafeCrudBean implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private List<Student> list;
    private Student item = new Student();
    private Student beforeEditItem = null;
    private boolean edit;
    
    @ManagedProperty(value="#{commonUtils}")
    private CommonUtils util;
    public void setUtil(CommonUtils util) {
        this.util = util;
    }
    
    @PostConstruct
    public void init() {
        list = new ArrayList<Student>();
    }
    
    public void add() {
        // DAO save the add
        item.setId(list.isEmpty() ? 1 : list.get(list.size() - 1).getId() + 1);
        list.add(item);
        item = new Student();
    
        util.redirectWithGet();
    }
    
    public void resetAdd() {
        item = new Student();
    
        util.redirectWithGet();
    }
    
    public void edit(Student item) {
        beforeEditItem = item.clone();
        this.item = item;
        edit = true;
    
        util.redirectWithGet();
    }
    
    public void cancelEdit() {
        this.item.restore(beforeEditItem);
        this.item = new Student();
        edit = false;
    
        util.redirectWithGet();
    }
    
    public void saveEdit() {
        // DAO save the edit
        this.item = new Student();
        edit = false;
    
        util.redirectWithGet();
    }
    
    public void delete(Student item) throws IOException {
        // DAO save the delete
        list.remove(item);
    
        util.redirectWithGet();
    }
    
    public List<Student> getList() {
        return list;
    }
    
    public Student getItem() {
        return this.item;
    }
    
    public boolean isEdit() {
        return this.edit;
    }
    
    
}

The "freshsafecrud.xhtml" is exactly the same as the "simplecrud.xhtml" file, except that it is bound to the "FreshsafeCrudBean" class. 

If you now load the "simplecrud.xhtml", you should feel free to refresh you page without seeing the side effects of the "POSTBACK". Of course the cost is a round trip to the web server.

Points of Interest

  • This is a simple CRUD example with JSF;
  • A true CRUD operation normally involves the database operations, but this example skipped them for simplicity;
  • JSF has a strong ASP.NET Web Form flavor, it heavily relies on "POSTBACK" for its functionalities;
  • An additional example is also provided to address the side effects of the "POSTBACK" with the cost of an additional server round-trip.

History

First Revision - 9/16/2015

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
I have been working in the IT industry for some time. It is still exciting and I am still learning. I am a happy and honest person, and I want to be your friend.

Comments and Discussions

 
QuestionJava+JPA+JSF tutorials Pin
Gerd Wagner17-Sep-15 5:50
professionalGerd Wagner17-Sep-15 5:50 
AnswerRe: Java+JPA+JSF tutorials Pin
Dr. Song Li17-Sep-15 5:55
Dr. Song Li17-Sep-15 5:55 
GeneralRe: Java+JPA+JSF tutorials Pin
Gerd Wagner23-Sep-15 7:41
professionalGerd Wagner23-Sep-15 7:41 
GeneralRe: Java+JPA+JSF tutorials Pin
Dr. Song Li25-Sep-15 3:43
Dr. Song Li25-Sep-15 3:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.