65.9K
CodeProject 正在变化。 阅读更多。
Home

JSF的简单CRUD示例

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2015 年 9 月 16 日

CPOL

3分钟阅读

viewsIcon

128227

downloadIcon

3669

这是一个使用 JSF 的简单 CRUD 示例。

引言

这是一个使用 JSF 的简单 CRUD 示例。

背景

JSF 是一个 MVC 框架,但它与 Spring MVC 和 ASP.NET MVC 有很大不同。 它实际上具有很强的 ASP.NET Web Form回发 (POSTBACK)" 风格。 此示例基于一个 Stack Overflow 示例

附件是一个 Maven 项目。 我已使用 Maven 3.2.1、Java 1.8.0_45、Tomcat 7 和 Eclipse Java EE IDE for Web Developers Luna Service Release 2 测试过它。 它实际上有两个示例。

  • “simplecrud.xhtml”是简单的 CRUD 示例;
  • “freshsafecrud.xhtml”用于解决由于 JSF 的“回发 (POSTBACK)”特性导致的问题。

“welcome.xhtml”是这两个示例的索引页面。

如果您不熟悉如何将 Maven 项目导入 Eclipse,您可以查看 此链接。 当我尝试导入项目时,我注意到 Eclipse 向我显示了一些错误消息,告诉我一些验证错误。 您可以简单地忽略这些消息并从 Eclipse 中删除标记。

pom.xml 和 web.xml

“pom.xml”声明了创建 JSF 应用程序所需的依赖项。

<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>

该示例应用程序的“web.xml”如下所示。

<?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>
            https://:8080/JSF-Example/
        </param-value>
    </context-param>
</web-app>
  • 从 servlet 容器的角度来看,所有 JSF 页面都将被映射到由“javax.faces.webapp.FacesServlet”类实现的单个 servlet;
  • 在此示例应用程序中,我们将所有对“xhtml”页面的请求映射到“javax.faces.webapp.FacesServlet”。

简单的 CRUD 示例

简单示例的 托管 bean 在“SimpleCrudBean”类中实现。

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;
    }
    
}
  • “add”、“saveEdit”和“delete”方法需要将数据与持久存储(如数据库)同步;
  • 在此示例中,为了简单起见,跳过了数据库操作。

“Student”类在“Student.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();
    }
}

“SimpleCrudBean”类绑定到“simplecrud.xhtml”文件,使其完全可用。

<!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>

如果您现在加载“simplecrud.xhtml”页面,您会发现 CRUD 操作都运行良好。 但是,如果您在添加学生后单击浏览器上的刷新按钮,您将看到这个难看的弹出消息。

如果您单击“继续”按钮并继续刷新,您会注意到刚添加的学生又被添加了。 这绝对不是一个好的行为。 这种行为是由于 JSF 的“回发 (POSTBACK)”特性造成的。 在下一个示例中,我们将尝试解决这个问题。

刷新安全的 CRUD 示例

为了解决简单 CRUD 示例中的问题,我创建了“CommonUtils”类。

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

“redirectWithGet”方法用于简单地向浏览器发送重定向请求,以使用 GET 请求刷新浏览器。“CommonUtils”对象被注入到“FreshsafeCrudBean”类中,并且每当执行“回发 (POSTBACK)”时都会调用“redirectWithGet”方法。

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

“freshsafecrud.xhtml”与“simplecrud.xhtml”文件完全相同,只是它绑定到“FreshsafeCrudBean”类。 

如果您现在加载“simplecrud.xhtml”,您应该可以随意刷新页面,而不会看到“回发 (POSTBACK)”的副作用。 当然,代价是往返 Web 服务器一次。

关注点

  • 这是一个使用 JSF 的简单 CRUD 示例;
  • 真正的 CRUD 操作通常涉及数据库操作,但此示例为了简单起见省略了这些操作;
  • JSF 具有很强的 ASP.NET Web Form 风格,它严重依赖“回发 (POSTBACK)”来实现其功能;
  • 还提供了一个额外的示例,以解决“回发 (POSTBACK)”的副作用,代价是额外的服务器往返。

历史

第一次修订 - 2015 年 9 月 16 日

© . All rights reserved.