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 EntityManagers 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 EntityManagers 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.