Cambridge Technology PartnersCommunity Documentation

Chapter 8. EntityHome

8.1. Prerequisites
8.2. EntityHome Usage
8.2.1. Inspecting the EntityHome class
8.3. Using EntityHome with JavaServer Faces
8.3.1. Installing EntityHome for JSF
8.3.2. Configuring EntityHome for JSF
8.3.3. Integrating EntityHome with JSF

In the first chapters we have laid a solid ground for querying entities. Connecting those entities to a user interface can be a tedious task - implementing common CRUD operations often include similar but still repetitive boilerplate code. CDI Query supports you here with the EntityHome API.

EntityHome requires a JPA 2 metamodel of the entities used with it. The metamodel can be generated with tools from Hibernate or EclipseLink, or typically almost any JPA 2 persistence provider of your choice.

A typical EntityHome looks like the following:

@Named

@Stateful
@ConversationScoped
public class BookHome extends EntityHome<Book, Long> {
    
    @Inject
    private BookDao bookDao;
    @Override
    protected EntityDao<Book, Long> getEntityDao() {
        return bookDao;
    }
    @Override
    protected List<SingularAttribute<Book, ?>> searchAttributes() {
        return singularAttributes()
                .addIfNotEmpty(getSearch().getName(), Book_.name)
                .getAttributes();
    }
}    

As you see, there is nothing more than overriding two simple methods:

A utility API supports creating the list of search attributes with fluent calls to a helper class.

Diving into the EntityHome class itself gives some insights in how it will connect to the frontend:

public abstract class EntityHome<E, PK> {

    // ACCESSORS AND MUTATORS
    public PK getId() { ... }
    public void setId(PK id) { ... }
    public E getEntity() { ... }
    public E getSearch() { ... }
    
    // METHODS
    public Object create() { ... }
    public void retrieve() { ... }
    public Object update() { ... }
    public Object delete() { ... }
    // PAGINATION
    public void search() { ... }
    public void paginate() { ... }
    public int getPage() { ...}
    public void setPage(int page) { ... }
    public long getCount() { ... }
    public int getPageSize() { ... }
    public List<E> getPageItems() { ... }
}        

All Home instances are able to store following attributes:

NameDescription
id PK id stores the primary key of the entity. This can be used with a page parameter identifying e.g. the entity to view, update or delete.
entity E entity holds an entity instance, e.g. on edit or create screens. The entity can be bound to UI components and getting populated with user input.
search E search holds also entity instance, but mainly to hold search parameters for querying page items. Changing to a create screen is assigning the search entity to the entity attribute to prepopulate the screen. This entity is never null.

In order to populate attributes and to actually do something with the data, following methods are available in the EntityHome class:

NameDescription
create create() initializes a conversation. This is not necessarily a JSF conversation, this is a UI specific concern.
retrieve retrieve() checks whether an id has been provided and retrieves the corresponding entity. Otherwise, the entity attribute is populated with the search entity.
update update() persists the current entity. Also used for new entities. It also ends the current conversation.
delete delete() deletes an entity identified by the id attribute. It also ends the current conversation.

Note that the return parameters are of type Object (resp. null by default) or void. This is overridden by specific UI framework modules, which we will show in the next chapter.

Other attributes and methods are concerned with entity search and pagination through the search results:

NameDescription


Paging Attributes

page int page holds the current page the user is on with regards to the current search result.
pageSize int pageSize corresponds to the max items displayed per page. This defaults to 10.
count long count counts the current number of search result entities.
pageItems List pageItems contains all entities which the current page holds. This is limited to pageSize items.


Paging Methods

search void search() initializes the search screen, which is just resetting the current page to 0.
paginate void paginate() does the lookup of the page items for the current page, also with recalculating the search result size.

JSF uses mainly String outcomes to determine navigation rules. Following methods produce these outcomes:

MethodOutcomeDescription
create()create?faces-redirect=true Redirects to a create page. Starts a new conversation (javax.enterprise.context.Conversation).
update()search?faces-redirect=true Redirects to a search page when the entity has been created. Ends the current conversation.
view?faces-redirect=true&id=PK Redirects to a page the entity can be viewed after updating it. Ends the current conversation.
null Returns null after an exception has occurred. Ends the current conversation.
delete()search?faces-redirect=true Redirects to a search page when the entity has been deleted. Ends the current conversation.
Returns null after an exception has occurred. Ends the current conversation.

Typically CRUD screens require three XHTML facelet views, which might look like the following snippets.

search.xhtml


<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    template="...">

    <!-- Reading URL parameters and reparing the page -->
    
    <f:metadata>
        <f:viewParam name="page" value="#{bookHome.page}"/>
        <f:event type="preRenderView" listener="#{bookHome.paginate}"/>
    </f:metadata>
    
    <!-- Search form sample -->
    
    <h:inputText id="searchName" value="#{bookHome.search.name}"/>
    
    <!-- Commands -->
    
    <h:commandLink value="Search" action="#{bookHome.search}"/>
    <h:commandLink value="Create New" action="#{bookHome.create}"/>
    
    <!-- Show search result -->
    
    <h:dataTable id="pageItems" value="#{bookHome.pageItems}" var="item">
        <h:column>
            <h:link outcome="/.../view">
                <f:param name="id" value="#{item.id}"/>
                <h:outputText id="itemName" value="#{item.name}"/>
            </h:link>
        </h:column>
    </h:dataTable>
    
    <!-- Pagination -->
    
    <h:panelGroup rendered="#{bookHome.count gt bookHome.pageSize}">
        <h:commandLink rendered="#{bookHome.page gt 0}">
            <f:param name="page" value="#{bookHome.page - 1}"/>
            Previous 
        </h:commandLink>
        <h:commandLink rendered="#{(bookHome.page + 1) * bookHome.pageSize lt bookHome.count}">
            <f:param name="page" value="#{bookHome.page + 1}"/>
            Next
        </h:commandLink>
    </h:panelGroup>
    
<ui:composition>
            

create.xhtml


<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    template="...">
    
    <!-- Reading URL parameters and initializing the page -->
    
    <f:metadata>
        <f:viewParam name="id" value="#{bookHome.id}" converter="javax.faces.Long"/>
        <f:event type="preRenderView" listener="#{bookHome.retrieve}"/>
    </f:metadata>
    
    <!-- Input Values -->
    
    <h:inputText id="bookName" value="#{bookHome.entity.name}"/>
    
    <!-- Commands -->
    
    <h:commandLink value="Save" action="#{bookHome.update}"/>
    
    <!-- Existing Entity -->
    
    <h:link value="Cancel" outcome="view">
        <f:param name="id" value="#{bookHome.id}"/>
    </h:link>
    <h:commandLink value="Delete" action="#{bookHome.delete}"/>
    
<ui:composition>
            

view.xhtml


<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    template="...">
    
    <!-- Reading URL parameters and initializing the page -->
    
    <f:metadata>
        <f:viewParam name="id" value="#{bookHome.id}" converter="javax.faces.Long"/>
        <f:event type="preRenderView" listener="#{bookHome.retrieve}"/>
    </f:metadata>
    
    <!-- Display the entity -->
    
<ui:composition>