I've been developing software for over 15 years working in Delphi and now Java. This site is a home for my open source projects, writings, articles and tutorials mainly focusing on Java and Java EE. Andy is a DZone MVB and is not an employee of DZone and has posted 34 posts at DZone. You can read more from them at their website. View Full User Profile

Conversational CRUD in Java EE 6

08.31.2010
| 10980 views |
  • submit to reddit

This tutorial will demonstrate a pattern for creating CRUD applications in JSF and Java EE 6. While this is not the only way of implementing this mechanism, it does promote re-use and can give you essentially zero code CRUD pages requiring just the view code. The goal is to provide a single structure that provides the particular feature of being both stateless or conversational where we might want a conversational edit page and a stateless view page. This pattern is based on the EntityHome pattern that was used in JBoss Seam and carries over well to Java EE 6 with CDI. This is something I use all the time to make view/edit pages really quickly and unlike most of the automatic scaffolding in other frameworks, doesn’t need re-writing to go into production.

Running in a Servlet Container

If you are running this in a servlet container or embedded Jetty, you will need to make the following changes :

  • Use the jee6-servlet-sandbox archetype which is only available in Knappsack 1.0.5 upwards.
  • Remove the @Stateless annotations from the EntityManagerDao
  • Add manual transactions to the entity manager method calls

The servlet version of the source code for this project can be downloaded from here (Crud App Maven Project Zip). Just unzip and type mvn jetty:run

CRUD is an initialism for Create, Retrieve, Update, Delete and is applied typically to entity objects or pieces of data in our application. CRUD probably accounts for about 80% of functions in most systems where users enter, update, view and sometimes delete data.

An entity object starts out by being created and then saved to the database. At a later date, the user may want to view the data and then modify and update the data. When the entity is no longer needed, the data will be deleted. This is the long term lifecycle of our data. When an entity is saved, it is saved with a key, or some reference value used to refer to that entity uniquely. Typically, this is an ID field that is often a number. The ID is assigned to the entity when it is saved and used to identify the entity when we want to retrieve, update or delete the entity.

Create our Application

We’ll start by creating a new application using the Knappsack Maven Archetypes. We’ll use the jee6-sandbox-archetype so we have some data to play with. Because the Knappsack archetypes are fairly new, you might have problems finding them in your IDEs depending on when you updated from Maven Central, so here is the command line to create the project :

mvn archetype:generate -DarchetypeGroupId=org.fluttercode.knappsack -DarchetypeArtifactId=jee6-sandbox-archetype
-DinteractiveMode=false -DarchetypeVersion=1.0.4 -DgroupId=org.fluttercode.demo -DartifactId=crudapp
-Dpackage=org.fluttercode.demo.crudapp

You can create the app and then import it into your IDE as needed. We are going to start by creating a very simple DAO style bean that will be used to load, save and refresh objects using the JPA entity manager. We will make it a stateless session bean so that it has transactional capabilities. If you are using the servlet container version, see the sidebar above for necessary changes. The downloadable source code uses the servlet container version so you can run it without an application server.

  1. Create a new class called EntityManagerDao
  2. Put the following code in the bean
    package org.fluttercode.demo.crudapp.bean;

    import javax.ejb.Stateless;
    import javax.inject.Inject;
    import javax.persistence.EntityManager;
    import java.io.Serializable;

    import org.fluttercode.demo.crudapp.qualifier.DataRepository;

    @Stateless
    public class EntityManagerDao implements Serializable {

    @Inject
    @DataRepository
    private EntityManager entityManager;

    public Object updateObject(Object object) {
    return entityManager.merge(object);
    }

    public void createObject(Object object) {
    entityManager.persist(object);
    }

    public void refresh(Object object) {
    entityManager.refresh(object);
    }

    public <T> T find(Class<T> clazz, Long id) {
    return entityManager.find(clazz, id);
    }

    public void deleteObject(Object object) {
    entityManager.remove(object);
    }

    }

    We inject a conversational entity manager into the bean and create methods for create, finding, updating, and removing entities. The DataRepository annotation is a Qualifier that is defined by the archetype and used to access the application’s default data source.

    All this bean really does is provide transaction support on our method calls to the entity manager. It is not a part of the key pattern, just a mechanism we can use to persist objects transactionally. While this Dao will work for any JPA entity, it is common practice to create Dao objects specific to individual types where you can add helper and additional methods specific to that type.

Our home bean

Now we have our support code set up, on to the main feature! We will create a generic home bean that will take an Id and when requested, load the entity bean for that Id. If no Id is available, then we assume a new entity is to be created. Once loaded or created, the entity will be accessible from our JSF pages where we can display or edit the information.

  1. Create a new class called org.fluttercode.demo.crudapp.bean.HomeBean. This will be our generic home bean that we use to load and hold our entities.
  2. Add the following code to make the bean generic and give it an id and entity attribute.
    package org.fluttercode.demo.crudapp.bean;

    import java.io.Serializable;
    import javax.inject.Inject;
    import org.fluttercode.demo.crudapp.model.BaseEntity;

    public class HomeBean<T extends BaseEntity> implements Serializable {

    @Inject
    private EntityManagerDao entityManagerDao;

    private Long id;
    private T instance;
    }
    Our bean will use the injected EntityManagerDao we just created. The BaseEntity class is a base entity class implemented in the archetype and has the common Id attribute.
  3. Now we want to add the getter method for the instance. What we want to do here is lazy load or create the bean when it is needed. The assumption is that you won’t request the bean until the home bean has been set up i.e. The id is properly set. The second assumption is that if you request the instance without the Id being set, you are actually creating a new instance.
  4. We’ll implement this in the getter that calls two other methods, one to create and return a new entity and another to load it. We’ll also add the getters and setters for the Id.
    public T getInstance() {
    if (instance == null) {
    if (id != null) {
    instance = loadInstance();
    } else {
    instance = createInstance();
    }
    }
    return instance;
    }

    public Long getId() {
    return id;
    }
    public void setId(Long id) {
    this.id = id;
    }
  5. The create or load methods are fairly simply but rely on some reflection magic to get the generic parameter type :
    public T loadInstance() {
    return entityManagerDao.find(getClassType(), getId());
    }

    public T createInstance() {
    try {
    return getClassType().newInstance();
    } catch (Exception e) {
    e.printStackTrace();
    }
    return null;
    }

    private Class<T> getClassType() {
    ParameterizedType parameterizedType = (ParameterizedType) getClass()
    .getGenericSuperclass();
    return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }
  6. When we edit our entity, we will want to either save the changes, or undo them. We will add two methods to do this, save and cancel and a third utility method called isManaged().
    public boolean isManaged() {
    return getInstance().getId() != null;
    }
    public String save() {
    if (isManaged()) {
    entityManagerDao.updateObject(getInstance());
    } else {
    entityManagerDao.createObject(getInstance());
    }
    conversation.end();
    return "saved";
    }

    public String cancel() {
    conversation.end();
    return "cancelled";
    }

    We end the conversation when we are done with the data which is when we either save or cancel the changes. Once the conversation is ended, it will be destroyed at the end of the request.
  7. One last method we want to add is called initConversation that checks to see if we are in a long running conversation, and starts one if we are not. In theory we could put this method anywhere, but here is a good a place as any. We also need to add a Conversation attribute into which we will inject the current conversation.
     	@Inject
    private Conversation conversation;
    ...
    ...
    ...
    public void initConversation() {
    if (conversation.isTransient()) {
    conversation.begin();
    }
    }

This wraps up the coding of our HomeBean that we will use as the backing bean for CRUD pages. Now let’s try out our new bean by implementing it for our Student entity class and create a page to view and edit our students.

  1. Start by creating a new class called org.fluttercode.demo.crudapp.view.StudentHome. We add two class level annotations, one to name the bean so JSF can access it through an EL expression and another to give it a scope.
    package org.fluttercode.demo.crudapp.view;

    import javax.enterprise.context.ConversationScoped;
    import javax.inject.Named;

    import org.fluttercode.demo.crudapp.bean.HomeBean;
    import org.fluttercode.demo.crudapp.model.Student;

    @Named
    @ConversationScoped
    public class StudentHome extends HomeBean<Student>{

    }
    We subclass the HomeBean and give it our Student class type to tell it what kind of entity we want it to handle. We have given this bean a conversation scope for reasons we’ll see later.
  2. For our view, create a new page called studentView.xhtml with the following content :
    <?xml version="1.0" encoding="UTF-8"?>
    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:fn="http://java.sun.com/jsp/jstl/functions"
    template="/WEB-INF/templates/template.xhtml">
    <ui:define name="content">
    <f:metadata>
    <f:viewParam name="id" value="#{studentHome.id}" />
    </f:metadata>
    <h:form>
    <h:outputText value="ID" styleClass="caption" />
    <h:outputText value="#{studentHome.instance.id}" styleClass="value" />

    <h:outputText value="First Name" styleClass="caption" />
    <h:outputText value="#{studentHome.instance.firstName}"
    styleClass="value" />

    <h:outputText value="Last Name" styleClass="caption" />
    <h:outputText value="#{studentHome.instance.lastName}"
    styleClass="value" />

    <h:outputText value="GPA" styleClass="caption" />
    <h:outputText value="#{studentHome.instance.gpa}" styleClass="value" />

    <h:link outcome="studentEdit.jsf" includeViewParams="true"
    value="Edit #{studentHome.instance.name}" style="margin-right : 24px" />
    <h:link outcome="studentEdit.jsf" value="Create Student" />
    </h:form>
    </ui:define>
    </ui:composition>


    Everything up to the f:metadata tag is boilerplate which includes pulling the template into the page. The f:metadata tag tells JSF to take a parameter called id and put it in the studentHome.id attribute. This is done before the page is rendered as part of our initial setup for the bean so we know which entity to load.

    In the view code we bind our text output components to attributes on the instance in the student home bean. We also styled the components so we can alter the display using CSS styles.
    At the bottom we have added a couple of links using the new JSF 2.0 h:link component to an as yet unwritten JSF page called studentEdit.xhtml. In the first link, we get JSF to pass in the view parameters automatically which in this case is the student Id value. Our second link is to create a new student which means we call the same studentEdit.xhtml page without passing the id as a parameter which we do by leavinging the includeViewParams attribute set to false.

    Note that the h:form tag isn’t necessary, but will make the next step easier and allow us to add form controls to this form should we want to.

  3. If you are running JBoss Developer Tools or Netbeans, you should be able to see the Student attributes in the auto-completion (very cool). If you open this page in a browser with an id (http://localhost:8080/crudapp/studentView.jsf?id=3) you will see the following person displayed : CRUD Pattern View Screenshot

    We did change two of the archetype CSS styles in the file screen.css to the following :

    .caption {
    float: left;
    width: 100px;
    padding-top : 2px;
    }

    .value {
    display : block;
    margin-bottom : 8px;
    margin-right: 8px;
    }

As you can see, we can display the student details for the given id. The page for editing this person is very similar, so similar in fact that we are going to start by copying the student view page and making 3 line changes and adding a page event.

  1. Copy the studentView.xhtml file and paste it in the same folder with the name studentEdit.xhtml. Open it up and make the following changes (highlighted in the source) :
    • Change the outputText components bound to attributes to inputText components.
    • Add the preRenderView event the the f:metadata tag.
    • Set the rendered attribute on the ID label and value since new entities don’t have a value to display
      <ui:define name="content">
      <f:metadata>
      <f:viewParam name="id" value="#{studentHome.id}" />
      <f:event type="preRenderView"
      listener="#{studentHome.initConversation}" />
      </f:metadata>
      <h:form>
      <h:outputText value="ID" styleClass="caption" rendered="#{studentHome.managed}"/>
      <h:outputText value="#{studentHome.instance.id}" styleClass="value" rendered="#{studentHome.managed}"/>

      <h:outputText value="First Name" styleClass="caption" />
      <h:inputText value="#{studentHome.instance.firstName}" styleClass="value" />

      <h:outputText value="Last Name" styleClass="caption" />
      <h:inputText value="#{studentHome.instance.lastName}" styleClass="value" />

      <h:outputText value="GPA" styleClass="caption" />
      <h:inputText value="#{studentHome.instance.gpa}" styleClass="value" />

      <h:commandButton value="Save" action="#{studentHome.save}" style="margin-right : 8px" />
      <h:commandButton value="Cancel" action="#{studentHome.cancel}" immediate="true" style="margin-right : 8px" />
      </h:form>
      </ui:define>

    If you launch this page now (http://localhost:8080/crudapp/studentEdit.jsf?id=3), you will see that we have an editor filled with the attribute values that can be changed.

    CRUD Pattern Edit Screenshot

That was pretty easy! The reason being is that we re-used the StudentHome bean in our edit page because it performs the same role in providing the requested entity based on the bean’s id value. We don’t need to tell the home bean that it is being used somewhere else because the page pulls the bean into the page.

Conversational or Not?

It’s important to notice that in the view page we do not call the method to initialize the conversation. Conversational beans that are not in a long running conversation are reduced to request scoped because the conversation context is destroyed at the end of the request if it is not marked as long running. This means that our view page is fairly stateless since the StudentHome bean used in this page is effectively request scoped. This is actually a good thing since when we press the refresh button, the page is regenerated from scratch instead of using an older version of the data held in the conversation.

On the other hand, the edit page does call the initConverstion method which starts a conversation. This means that the edit page is conversational which is good because it carries the state from one request to the next without relying on our page to support that state or having to reload it each time. Without the conversation we would have to manually propagate the id value from one request to the next, and possibly other values that are a part of the entity but not edited on the page. For example, if we had an addedDate attribute, we might want to display it, but not edit it, but without conversations we have to resort to either putting it in a hidden field in the form, or re-loading the object to get the value on postback. Otherwise how can we have the right value when we save the edited entity to the database?

This dual nature of the conversation scope is a very useful feature of CDI (and Seam before it) because it allows one bean to act in two different scopes depending on whether the conversation is long running or not which is ideal for view and edit pages.

This is a simple design that can be re-used anywhere you need to edit an entity with little or no code changes.

You can download the servlet version of the demo project from here (Crud App Maven Project Zip). Just unzip it and run mvn jetty:run

Additional Points

Here are some addition points that are not a core part of the demonstration but flesh things out a bit more. All these changes are included in the maven project that can be downloaded.

Do we need an Entity Manager DAO?

Here we used a simple DAO to wrap calls to the EntityManager. There’s no reason you couldn’t make your Entity Home bean an EJB, inject the entity manager and persist the entities directly. With a separate entity manager DAO it makes writing code for servlet containers cleaner since the explicit transactions are bundled in the DAO. Also, it is possible to write one DAO bean for Java EE 6 servers and another for Servlet containers and switch between them using @Alternative.

Deleting is a part of CRUD

I left deleting out primarily because it is fairly simple and the focus was really on the retrieve and update pieces which is often trickier. To implement delete, we add a method to the HomeBean to end the conversation and delete the item.

public String delete() {
entityManagerDao.deleteObject(getInstance());
conversation.end();
return "deleted";
}
Now we just need to add a button to call the delete method from the edit page.
<h:commandButton value="Delete" action="#{studentHome.delete}"/>
When we click the button, we delete the entity, and return the action value “deleted” for which, in the next section, we’ll add navigation to the home page.

Finding your way around

We haven’t included any navigation here so when you save or cancel changes, you stay on the same page. So far I’ve left it out because it isn’t central to the CRUDness of the tutorial. However, we can easily add some navigation in faces-config.xml.

<navigation-rule>
<from-view-id>/studentEdit.xhtml</from-view-id>
<navigation-case>
<from-outcome>saved</from-outcome>
<to-view-id>/studentView.xhtml</to-view-id>
<redirect include-view-params="true" />
</navigation-case>

<navigation-case>
<from-outcome>cancelled</from-outcome>
<if>#{studentHome.managed}</if>
<to-view-id>/studentView.xhtml</to-view-id>
<redirect include-view-params="true" />
</navigation-case>

<navigation-case>
<from-outcome>cancelled</from-outcome>
<if>#{!studentHome.managed}</if>
<to-view-id>/home.xhtml</to-view-id>
<redirect include-view-params="true" />
</navigation-case>

<navigation-case>
<from-outcome>deleted</from-outcome>
<to-view-id>/home.xhtml</to-view-id>
</navigation-case>

</navigation-rule>


Notice that when we cancel the changes, we go to either the view page or the home page depending on whether the entity exists in the database.

Better Homes

Let’s also add on a better home page so you can see and select different students. Add the following method to the DataFactory.java class that comes with the archetype.

@Produces
@Named("students")
public List<Student> getStudents() {
List<Student> students = entityManager.createQuery(
"select s from Student s").setMaxResults(20)
.getResultList();
return students;
}

Open up the the home.xhtml page and change the content to :

<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
template="/WEB-INF/templates/template.xhtml">
<ui:define name="content">
<h1>Courses</h1>
<h:form>
<h:dataTable value="#{students}" var="v_student"
styleClass="dataTable" rowClasses="odd,even">
<h:column>
<f:facet name="header">Id
</f:facet>
#{v_student.id}
</h:column>

<h:column>
<f:facet name="header">Name
</f:facet>
<h:link outcome="studentView.jsf?id=#{v_student.id}"
value="#{v_student.name}" />
</h:column>

</h:dataTable>
</h:form>
<h:link outcome="studentEdit.jsf" value="Create Student" />

</ui:define>
</ui:composition>

This will list all the students and let you edit them or add new ones. The project source that can be downloaded (Crud App Maven Project Zip) includes these changes plus validation error messages on controls.

From http://www.andygibson.net/blog/tutorial/pattern-for-conversational-crud-in-java-ee-6/

Published at DZone with permission of Andy Gibson, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Cosmin Mutu replied on Tue, 2010/08/31 - 12:55am

very nice post

Jerry Zhang replied on Tue, 2010/08/31 - 11:35pm

Very nice tutorial. But I wonder, is this the elegance after 6 generation of JEE improvments?

King Sam replied on Fri, 2012/02/24 - 9:57am

Thanks for your posts and articles, they have been very helpful in learning javaee, I also like your critical and contextual approach. I have some comments and questions below, generally in increasing scope order.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.