The scenario is based on the full-profile Java EE 7 (Wildfly 8) and uses EJB 3.2, JPA 2.1, JSF 2.2 (Mojarra, Primefaces library) and the following project structure:
- Web project
- EAR project
- EJB project
- JPA project
The basic CRUD scenario looks as follows:
- User enters the page or clicks New, enters item name and clicks Save > item is saved in database and added to table
- User selects item from table > item name field is updated with appropriate value from currently selected item
- User selects item from table, modifies it and clicks save > modified item is updated in database and table
- User selects item from table and clicks Delete > item is removed from database and table
item-overview.xhtml
<h:form id="itemPanel">
<p:panelGrid id="itemPanelGrid" columns="2">
<f:facet name="header">Item</f:facet>
<h:outputLabel for="itemName" value="Item name: " />
<p:inputText id="itemName" value="#{itemController.item.itemName}"></p:inputText>
<f:facet name="footer">
<p:commandButton id="newItem" value="New" update="itemPanelGrid"
process="@this" actionListener="#{itemController.newItem}">
<p:resetInput target="itemPanelGrid" />
</p:commandButton>
<p:commandButton id="deleteItem" value="Delete"
update="itemsPanel @form"
actionListener="#{itemController.deleteItem}" />
<p:commandButton id="saveItem" value="Save"
update="itemsPanel @form" action="#{itemController.saveItem}" />
</f:facet>
</p:panelGrid>
<p:panel id="itemsPanel">
<p:dataTable id="itemTable" var="tableItem" selectionMode="single"
selection="#{itemController.selectedItem}" rowKey="#{tableItem.id}"
emptyMessage="No items available" value="#{itemController.items}">
<p:ajax event="rowSelect" listener="#{itemController.onRowSelect}"
update="@this @form" />
<p:column headerText="Id">
<h:outputText value="#{tableItem.id}" />
</p:column>
<p:column headerText="Item name">
<h:outputText value="#{tableItem.itemName}" />
</p:column>
</p:dataTable>
</p:panel>
</h:form>
ItemController.java
public class ItemController implements Serializable {
@EJB
private ItemServiceBean itemServiceBean;
private Item item;
private Item selectedItem;
private List<Item> items;
@PostConstruct
public void init() {
item = new Item();
items = itemServiceBean.getAllItems();
}
public void deleteItem() {
if (items.contains(item))
items.remove(item);
itemServiceBean.deleteItem(item);
}
public String saveItem() {
if (!items.contains(item))
items.add(item);
itemServiceBean.saveItem(item);
return null;
}
public void newItem() {
item = new Item();
}
public List<Item> getItems() {
return items;
}
public void setItems(List<Item> items) {
this.items = items;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
public Item getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(Item selectedItem) {
this.selectedItem = selectedItem;
}
public void onRowSelect(SelectEvent event) {
FacesMessage msg = new FacesMessage("Item selected", ((Item) event.getObject()).getId() + "");
FacesContext.getCurrentInstance().addMessage(null, msg);
this.item = this.selectedItem;
}
}
ItemServiceBean.java
public class ItemServiceBean {
@PersistenceContext
private EntityManager entityManager;
public ItemServiceBean() { }
public void saveItem(Item item) {
entityManager.persist(item);
}
public void deleteItem(Item item) {
Item managedItem = getItem(item);
entityManager.remove(managedItem);
}
public Item updateItem(Item item) {
Item managedItem = getItem(item);
return entityManager.merge(managedItem);
}
public List<Item> getAllItems() {
TypedQuery<Item> query = entityManager.createQuery("SELECT i FROM Item i", Item.class);
return query.getResultList();
}
public Item getItem(Item item) {
return entityManager.find(Item.class, item.getId());
}
}
The backing bean is @ViewScoped. The EJB behind is stateless and I suppose it is a container-managed bean (as it is the default setting).
If you try to delete an item using EntityManager
s remove
method and simply pass the currently selected item, the object is detached. So, what I did is retrieving the item from database, putting it into the persistence context and removing it. I had expected it to be a little different without needing to retrieve the object from the database, putting it into the context again and removing it. Is there a possibility to avoid these additional steps?
I have had several tries on updating an already existing item. If you ask the persistence context if the modified item is already managed using the EntityManager
s contains
method it turns out that the item is not in the persistence context. So, once again, I needed to retrieve the object from the database and update it using merge
afterwards. I really doubt that this is the preferred way and would be glad if someone has any hints on this.
I was assuming that the whole transaction remains "open" and the object/s remain/s attached to the persistence context.
Thank you.