Add new/Update/View Details can be combined altogether into one single screen. This results in the CRUD operations to only two screens. The search screen has the "delete" action as the last column of the datatable.
Here's the updated search screen(it shows primefaces database pagination attributes: paginatorPosition="bottom" rows="5" rowsPerPageTemplate="5,10,20,50"):
[img]http://dl.iteye.com/upload/attachment/0070/8105/662e964d-9421-3ec3-8f0f-201f1c70d7a8.png[/img]
Here's the Add/Update/View Details screen(it shows the Primefaces <p:calendar/> widget):
[img]http://dl.iteye.com/upload/attachment/0070/8092/6fa8194c-37b3-39d5-84eb-4cbefb51f4a3.png[/img]
Updated source "/student/studentSearch.xhtml":
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>student search</title>
</h:head>
<h:body>
<h:form id="searchForm">
<p:panel header="Student Search Form">
<h:panelGrid columns="3">
<h:outputLabel value="Name: "/>
<h:inputText value="#{ss.nameFilter}"></h:inputText>
<h:commandButton type="submit"
value="Search"
action="#{ss.findByName}"/>
</h:panelGrid>
</p:panel>
</h:form>
<p:panel header="Students Found: #{ss.size}">
<h:form id="dataListForm">
<!-- search result list -->
<p:dataTable id="idStuDataTable"
var="st" value="#{ss.searchResultList}"
paginator="true" paginatorPosition="bottom"
rows="5" rowsPerPageTemplate="5,10,20,50">
<p:column headerText="name" sortBy="#{st.name}">
<h:outputText value="#{st.name}" />
</p:column>
<p:column headerText="mobile" sortBy="#{st.mobile}">
<h:outputText value="#{st.mobile}" />
</p:column>
<p:column headerText="created date" sortBy="#{st.created_date}">
<h:outputText value="#{st.created_date}">
<f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss" timeZone="#{ss.timeZone}"/>
</h:outputText>
</p:column>
<p:column headerText="action" style="width:12%">
<h:commandLink id="idViewDetailsButton" title="View Details"
value="details" action="studentDetails.xhtml">
<f:setPropertyActionListener value="#{st}" target="#{stbean.student}" />
</h:commandLink>
<p:spacer width="4"/>
<h:commandLink id="idDeleteButton" title="Delete Student"
value="delete" action="#{stbean.delete}"
onclick="return confirm('Are you sure to delete: #{st.name}?')">
<f:setPropertyActionListener value="#{st}" target="#{stbean.student}" />
<f:setPropertyActionListener value="true" target="#{ss.refresh}" />
</h:commandLink>
</p:column>
</p:dataTable>
</h:form>
</p:panel>
<h:form id="addNewFrm">
<h:commandButton id="newButt" value="Add New Student" action="studentDetails.xhtml"/>
</h:form>
</h:body>
</html>
Updated backing bean "StudentSearch.java"
package com.jxee.action.student;
import java.io.Serializable;
import java.util.List;
import java.util.TimeZone;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.apache.log4j.Logger;
import com.jxee.ejb.student.StudentDAO;
import com.jxee.model.student.Student;
@ManagedBean(name="ss")
@SessionScoped //@ViewScoped not working with Primefaces <p:dataTable/> sorting.
public class StudentSearch implements Serializable {
private static final Logger log = Logger.getLogger(StudentSearch.class);
private static final String SEARCH_PAGE = "/student/studentSearch.xhtml";
private List<Student> searchResultList;
private @EJB StudentDAO dao;
private String nameFilter;
private int maxRows = 50;
/**
* flag if cached result list needs reloading, after add/update/delete etc.
* set from views: <f:setPropertyActionListener value="true" target="#{ss.refresh}" />
*/
private boolean refresh;
public boolean isRefresh() {
return refresh;
}
public void setRefresh(boolean refresh) {
log.debug("-- setting refresh: " + refresh);
this.refresh = refresh;
}
public String findByName() {
log.debug("search student by nameFilter: " + nameFilter);
searchResultList = dao.find(this.nameFilter, maxRows);
return SEARCH_PAGE;
}
public String getNameFilter() {
return nameFilter;
}
public void setNameFilter(String afilter) {
this.nameFilter = afilter;
}
public int getMaxRows() {
return maxRows;
}
public void setMaxRows(int maxRows) {
this.maxRows = maxRows;
}
public List<Student> getSearchResultList() {
log.debug("--- getting search result list.... isRefresh? " + this.isRefresh());
if(this.isRefresh()) {
this.findByName();
this.refresh = false;
}
return this.searchResultList;
}
public void setSearchResultList(List<Student> searchResultList) {
this.searchResultList = searchResultList;
}
public int getSize() {
if(this.isRefresh()) { // in order to get the correct list size
this.findByName();
this.refresh = false;
}
int size = this.searchResultList != null ? this.searchResultList.size() : 0;
log.debug("search result size: " + size);
return size;
}
// use default TimeZone to display time properly
public TimeZone getTimeZone() {
TimeZone tz = TimeZone.getDefault();
return tz;
}
}
Source "/student/studentDetails.xhmtl":
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>save student</title>
</h:head>
<h:body>
<h:form id="formStudent">
<p:panel header="Student Details" style="width:50%">
<h:panelGrid columns="3">
Name: <h:inputText id="sname" value="#{stbean.student.name}"/>
<p:message for="sname"/>
Age: <h:inputText id="sage" value="#{stbean.student.age}"/>
<p:message for="sage"/>
Mobile: <h:inputText id="smobile" value="#{stbean.student.mobile}"/>
<p:message for="smobile"/>
Created Date: <p:calendar id="screateddate"
pattern="yyyy-MM-dd HH:mm:ss"
value="#{stbean.student.created_date}"/>
<p:message for="screateddate"/>
<h:inputHidden value="#{stbean.student.id}"/>
</h:panelGrid>
<p:separator/>
<h:commandButton id="saveBut" value="Save" action="#{stbean.save}">
<f:setPropertyActionListener value="true" target="#{ss.refresh}" />
</h:commandButton>
<p:spacer width="10"/>
<h:commandButton id="cancelBut" value="Cancel"
immediate="true" action="studentSearch.xhtml"/>
</p:panel>
</h:form>
</h:body>
</html>
Source backing bean "StudentBean.java":
package com.jxee.action.student;
import java.io.Serializable;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import org.apache.log4j.Logger;
import com.jxee.ejb.student.StudentDAO;
import com.jxee.model.student.Student;
@ManagedBean(name="stbean")
public class StudentBean implements Serializable {
private static final Logger log = Logger.getLogger(StudentBean.class);
private static final String SEARCH_PAGE = "/student/studentSearch.xhtml";
private @EJB StudentDAO dao;
// property set from Primefaces single row selection
private Student student = new Student();
public Student getStudent() {
return student;
}
public void setStudent(Student st) {
this.student = st;
}
public String save() {
log.debug("saving student: " + this.student);
if(student != null ) {
if(student.getId() != null && student.getId() > 0) {
log.debug("updating student: " + student);
dao.update(student);
}
else {
log.debug("adding new student: " + student);
dao.add(student);
}
}
return SEARCH_PAGE;
}
public String delete() {
if(student != null) {
log.debug("deleting student: " + student);
dao.delete(this.student);
}
return SEARCH_PAGE;
}
public String cancel() {
return SEARCH_PAGE;
}
}
Updated dao ejb "StudnetDAOImp.java":
package com.jxee.ejb.student;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.log4j.Logger;
import com.jxee.ejb.UserDAOImp;
import com.jxee.model.student.Student;
@Stateless
public class StudentDAOImp implements StudentDAO {
private static final Logger log = Logger.getLogger(UserDAOImp.class);
@PersistenceContext(unitName="punit.projee6")
private EntityManager em;
@SuppressWarnings("unchecked")
public List<Student> find(String nameFilter, int max) {
String pattern = nameFilter != null ? "%" + nameFilter + "%" : "%";
String sql = "select s from Student s where s.name like :pattern";
Query q = em.createQuery(sql);
q.setParameter("pattern", pattern);
q.setFirstResult(0);
q.setMaxResults(max);
return q.getResultList();
}
public void add(Student stu) {
log.debug("adding new student: " + stu);
if(stu.getId() != null) {
// set null to avoid exception:
// "org.hibernate.PersistentObjectException: detached entity passed to persist"
stu.setId(null);
}
em.persist(stu);
}
public void update(Student stu) {
log.debug("updating student: " + stu);
em.merge(stu);
}
public void delete(Student stu) {
Student todel = em.find(Student.class, stu.getId());
log.debug("student loaded to delete: " + todel);
em.remove(todel);
}
}
Now that it has a search page, a view details/add new/update page and the delete functionality as well. This completes the CRUD experiment based on the "Student" entity in the database.
What to be noted on this experiment is that:
1. Although JSF 2.0 provides a couple of ways of binding http GET parameters, it still looks odd and unnatural if you like. In this example, i used <f:setPropertyActionListener/> to bind a reload-after-add/update/delete parameter "refresh". I'll add a new blog shortly for how JSF2.0 bind http GET parameters.
2. They said that the backing bean of the primefaces "<p:dataTable/>" should be @ViewScoped to make sorting work. My experiment is that it needs to be @SessionScoped.