前回までは IDE を使わずに JavaEE6 を触って見ましたが、普通はやっぱり IDE 使うわけで、
blog1.mammb.com
エンティテイの作成
作成したプロジェクト選択して、新規 > その他 でエンティテイ・クラス を選択
Bookクラスが出来るので、属性を追加して
private String title; private String description;
自動生成された EJB
ステートレスセッションビーン として BookFacade が作られる
@Stateless public class BookFacade extends AbstractFacade<Book> { @PersistenceContext(unitName = "SampleAppPU") private EntityManager em; @Override protected EntityManager getEntityManager() { return em; } public BookFacade() { super(Book.class); } }
で、BookFacade の親の AbstractFacade で EntityManager 操作が実装されてる
public abstract class AbstractFacade<T> { private Class<T> entityClass; public AbstractFacade(Class<T> entityClass) { this.entityClass = entityClass; } protected abstract EntityManager getEntityManager(); public void create(T entity) { getEntityManager().persist(entity); } public void edit(T entity) { getEntityManager().merge(entity); } public void remove(T entity) { getEntityManager().remove(getEntityManager().merge(entity)); } public T find(Object id) { return getEntityManager().find(entityClass, id); } public List<T> findAll() { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); return getEntityManager().createQuery(cq).getResultList(); } public List<T> findRange(int[] range) { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); javax.persistence.Query q = getEntityManager().createQuery(cq); q.setMaxResults(range[1] - range[0]); q.setFirstResult(range[0]); return q.getResultList(); } public int count() { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); javax.persistence.criteria.Root<T> rt = cq.from(entityClass); cq.select(getEntityManager().getCriteriaBuilder().count(rt)); javax.persistence.Query q = getEntityManager().createQuery(cq); return ((Long) q.getSingleResult()).intValue(); } }
EntityManager 経由で CRUD
自動生成された JSF
Model 部分
@Named("bookController") @SessionScoped public class BookController implements Serializable { private Book current; private DataModel items = null; @EJB private etc9.BookFacade ejbFacade; private PaginationHelper pagination; private int selectedItemIndex; public BookController() { } public Book getSelected() { if (current == null) { current = new Book(); selectedItemIndex = -1; } return current; } private BookFacade getFacade() { return ejbFacade; } public PaginationHelper getPagination() { if (pagination == null) { pagination = new PaginationHelper(10) { @Override public int getItemsCount() { return getFacade().count(); } @Override public DataModel createPageDataModel() { return new ListDataModel(getFacade().findRange(new int[]{getPageFirstItem(), getPageFirstItem() + getPageSize()})); } }; } return pagination; } public String prepareList() { recreateModel(); return "List"; } public String prepareView() { current = (Book) getItems().getRowData(); selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex(); return "View"; } public String prepareCreate() { current = new Book(); selectedItemIndex = -1; return "Create"; } public String create() { try { getFacade().create(current); JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("BookCreated")); return prepareCreate(); } catch (Exception e) { JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured")); return null; } } public String prepareEdit() { current = (Book) getItems().getRowData(); selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex(); return "Edit"; } public String update() { try { getFacade().edit(current); JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("BookUpdated")); return "View"; } catch (Exception e) { JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured")); return null; } } public String destroy() { current = (Book) getItems().getRowData(); selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex(); performDestroy(); recreatePagination(); recreateModel(); return "List"; } public String destroyAndView() { performDestroy(); recreateModel(); updateCurrentItem(); if (selectedItemIndex >= 0) { return "View"; } else { // all items were removed - go back to list recreateModel(); return "List"; } } private void performDestroy() { try { getFacade().remove(current); JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("BookDeleted")); } catch (Exception e) { JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured")); } } private void updateCurrentItem() { int count = getFacade().count(); if (selectedItemIndex >= count) { // selected index cannot be bigger than number of items: selectedItemIndex = count - 1; // go to previous page if last page disappeared: if (pagination.getPageFirstItem() >= count) { pagination.previousPage(); } } if (selectedItemIndex >= 0) { current = getFacade().findRange(new int[]{selectedItemIndex, selectedItemIndex + 1}).get(0); } } public DataModel getItems() { if (items == null) { items = getPagination().createPageDataModel(); } return items; } private void recreateModel() { items = null; } private void recreatePagination() { pagination = null; } public String next() { getPagination().nextPage(); recreateModel(); return "List"; } public String previous() { getPagination().previousPage(); recreateModel(); return "List"; } public SelectItem[] getItemsAvailableSelectMany() { return JsfUtil.getSelectItems(ejbFacade.findAll(), false); } public SelectItem[] getItemsAvailableSelectOne() { return JsfUtil.getSelectItems(ejbFacade.findAll(), true); } @FacesConverter(forClass = Book.class) public static class BookControllerConverter implements Converter { public Object getAsObject(FacesContext facesContext, UIComponent component, String value) { if (value == null || value.length() == 0) { return null; } BookController controller = (BookController) facesContext.getApplication().getELResolver(). getValue(facesContext.getELContext(), null, "bookController"); return controller.ejbFacade.find(getKey(value)); } java.lang.Long getKey(String value) { java.lang.Long key; key = Long.valueOf(value); return key; } String getStringKey(java.lang.Long value) { StringBuffer sb = new StringBuffer(); sb.append(value); return sb.toString(); } public String getAsString(FacesContext facesContext, UIComponent component, Object object) { if (object == null) { return null; } if (object instanceof Book) { Book o = (Book) object; return getStringKey(o.getId()); } else { throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + Book.class.getName()); } } } }
- ちょっと長い。ページからの各Actionが実装されてる
- PaginationHelper とやらでページング
- ResourceBundle で文言外出し
- JsfUtil なるユーティリティでページ用の boilerplate を扱っている
- FacesConverter にてリクエストのコンバータ処理が実装されている
PaginationHelper
package etc9.util; import javax.faces.model.DataModel; public abstract class PaginationHelper { private int pageSize; private int page; public PaginationHelper(int pageSize) { this.pageSize = pageSize; } public abstract int getItemsCount(); public abstract DataModel createPageDataModel(); public int getPageFirstItem() { return page * pageSize; } public int getPageLastItem() { int i = getPageFirstItem() + pageSize - 1; int count = getItemsCount() - 1; if (i > count) { i = count; } if (i < 0) { i = 0; } return i; } public boolean isHasNextPage() { return (page + 1) * pageSize + 1 <= getItemsCount(); } public void nextPage() { if (isHasNextPage()) { page++; } } public boolean isHasPreviousPage() { return page > 0; } public void previousPage() { if (isHasPreviousPage()) { page--; } } public int getPageSize() { return pageSize; } }
JsfUtil
public class JsfUtil { public static SelectItem[] getSelectItems(List<?> entities, boolean selectOne) { int size = selectOne ? entities.size() + 1 : entities.size(); SelectItem[] items = new SelectItem[size]; int i = 0; if (selectOne) { items[0] = new SelectItem("", "---"); i++; } for (Object x : entities) { items[i++] = new SelectItem(x, x.toString()); } return items; } public static void addErrorMessage(Exception ex, String defaultMsg) { String msg = ex.getLocalizedMessage(); if (msg != null && msg.length() > 0) { addErrorMessage(msg); } else { addErrorMessage(defaultMsg); } } public static void addErrorMessages(List<String> messages) { for (String message : messages) { addErrorMessage(message); } } public static void addErrorMessage(String msg) { FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg); FacesContext.getCurrentInstance().addMessage(null, facesMsg); } public static void addSuccessMessage(String msg) { FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg); FacesContext.getCurrentInstance().addMessage("successInfo", facesMsg); } public static String getRequestParameter(String key) { return FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(key); } public static Object getObjectFromRequestParameter(String requestParameterName, Converter converter, UIComponent component) { String theId = JsfUtil.getRequestParameter(requestParameterName); return converter.getAsObject(FacesContext.getCurrentInstance(), component, theId); } }
ページ
template.xhtml がページのテンプレ
<?xml version='1.0' encoding='UTF-8' ?> <!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title><ui:insert name="title">Default Title</ui:insert></title> <h:outputStylesheet name="css/jsfcrud.css"/> </h:head> <h:body> <h1> <ui:insert name="title">Default Title</ui:insert> </h1> <p> <ui:insert name="body">Default Body</ui:insert> </p> </h:body> </html>
一覧ページ
<?xml version="1.0" encoding="UTF-8" ?> <!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <ui:composition template="/template.xhtml"> <ui:define name="title"> <h:outputText value="#{bundle.ListBookTitle}"></h:outputText> </ui:define> <ui:define name="body"> <h:form styleClass="jsfcrud_list_form"> <h:panelGroup id="messagePanel" layout="block"> <h:messages errorStyle="color: red" infoStyle="color: green" layout="table"/> </h:panelGroup> <h:outputText escape="false" value="#{bundle.ListBookEmpty}" rendered="#{bookController.items.rowCount == 0}"/> <h:panelGroup rendered="#{bookController.items.rowCount > 0}"> <h:outputText value="#{bookController.pagination.pageFirstItem + 1}..#{bookController.pagination.pageLastItem + 1}/#{bookController.pagination.itemsCount}"/> <h:commandLink action="#{bookController.previous}" value="#{bundle.Previous} #{bookController.pagination.pageSize}" rendered="#{bookController.pagination.hasPreviousPage}"/> <h:commandLink action="#{bookController.next}" value="#{bundle.Next} #{bookController.pagination.pageSize}" rendered="#{bookController.pagination.hasNextPage}"/> <h:dataTable value="#{bookController.items}" var="item" border="0" cellpadding="2" cellspacing="0" rowClasses="jsfcrud_odd_row,jsfcrud_even_row" rules="all" style="border:solid 1px"> <h:column> <f:facet name="header"> <h:outputText value="#{bundle.ListBookTitle_title}"/> </f:facet> <h:outputText value="#{item.title}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="#{bundle.ListBookTitle_description}"/> </f:facet> <h:outputText value="#{item.description}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="#{bundle.ListBookTitle_id}"/> </f:facet> <h:outputText value="#{item.id}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value=" "/> </f:facet> <h:commandLink action="#{bookController.prepareView}" value="#{bundle.ListBookViewLink}"/> <h:outputText value=" "/> <h:commandLink action="#{bookController.prepareEdit}" value="#{bundle.ListBookEditLink}"/> <h:outputText value=" "/> <h:commandLink action="#{bookController.destroy}" value="#{bundle.ListBookDestroyLink}"/> </h:column> </h:dataTable> </h:panelGroup> <br /> <h:commandLink action="#{bookController.prepareCreate}" value="#{bundle.ListBookCreateLink}"/> <br /> <br /> <h:link outcome="/index" value="#{bundle.ListBookIndexLink}"/> </h:form> </ui:define> </ui:composition> </html>
編集ページ
<?xml version="1.0" encoding="UTF-8" ?> <!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <ui:composition template="/template.xhtml"> <ui:define name="title"> <h:outputText value="#{bundle.EditBookTitle}"></h:outputText> </ui:define> <ui:define name="body"> <h:panelGroup id="messagePanel" layout="block"> <h:messages errorStyle="color: red" infoStyle="color: green" layout="table"/> </h:panelGroup> <h:form> <h:panelGrid columns="2"> <h:outputLabel value="#{bundle.EditBookLabel_title}" for="title" /> <h:inputText id="title" value="#{bookController.selected.title}" title="#{bundle.EditBookTitle_title}" /> <h:outputLabel value="#{bundle.EditBookLabel_description}" for="description" /> <h:inputText id="description" value="#{bookController.selected.description}" title="#{bundle.EditBookTitle_description}" /> <h:outputLabel value="#{bundle.EditBookLabel_id}" for="id" /> <h:inputText id="id" value="#{bookController.selected.id}" title="#{bundle.EditBookTitle_id}" /> </h:panelGrid> <h:commandLink action="#{bookController.update}" value="#{bundle.EditBookSaveLink}"/> <br /> <br /> <h:link outcome="View" value="#{bundle.EditBookViewLink}"/> <br /> <h:commandLink action="#{bookController.prepareList}" value="#{bundle.EditBookShowAllLink}" immediate="true"/> <br /> <br /> <h:link outcome="/index" value="#{bundle.EditBookIndexLink}" /> </h:form> </ui:define> </ui:composition> </html>
登録ページ
<?xml version="1.0" encoding="UTF-8" ?> <!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:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <ui:composition template="/template.xhtml"> <ui:define name="title"> <h:outputText value="#{bundle.CreateBookTitle}"></h:outputText> </ui:define> <ui:define name="body"> <h:panelGroup id="messagePanel" layout="block"> <h:messages errorStyle="color: red" infoStyle="color: green" layout="table"/> </h:panelGroup> <h:form> <h:panelGrid columns="2"> <h:outputLabel value="#{bundle.CreateBookLabel_title}" for="title" /> <h:inputText id="title" value="#{bookController.selected.title}" title="#{bundle.CreateBookTitle_title}" /> <h:outputLabel value="#{bundle.CreateBookLabel_description}" for="description" /> <h:inputText id="description" value="#{bookController.selected.description}" title="#{bundle.CreateBookTitle_description}" /> <h:outputLabel value="#{bundle.CreateBookLabel_id}" for="id" /> <h:inputText id="id" value="#{bookController.selected.id}" title="#{bundle.CreateBookTitle_id}" /> </h:panelGrid> <br /> <h:commandLink action="#{bookController.create}" value="#{bundle.CreateBookSaveLink}" /> <br /> <br /> <h:commandLink action="#{bookController.prepareList}" value="#{bundle.CreateBookShowAllLink}" immediate="true"/> <br /> <br /> <h:link outcome="/index" value="#{bundle.CreateBookIndexLink}"/> </h:form> </ui:define> </ui:composition> </html>
といったところ