该场景基于完整的Java EE 7(Wildfly 8)并使用EJB 3.2,JPA 2.1,JSF 2.2(Mojarra,Primefaces库)和以下项目结构:
基本的CRUD场景如下:
项-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());
}
}
支持bean是@ViewScoped。后面的EJB是无状态的,我想它是一个容器管理的bean(因为它是默认设置)。
如果您尝试使用EntityManager
s remove
方法删除项目并只是传递当前选定的项目,则会分离该对象。所以,我所做的是从数据库中检索项目,将其放入持久化上下文并删除它。我原本以为它有点不同,无需从数据库中检索对象,再次将其放入上下文并将其删除。是否有可能避免这些额外的步骤?
我已多次尝试更新现有项目。如果您询问持久化上下文是否已使用EntityManager
contains
方法管理已修改的项目,则事实证明该项目不在持久性上下文中。因此,我需要再次从数据库中检索对象,然后使用merge
更新它。我真的怀疑这是首选的方式,如果有人对此有任何暗示,我会很高兴。
我假设整个交易仍然是&#34;开放&#34;并且object / s保持/ s附加到持久化上下文。
谢谢。
答案 0 :(得分:1)
如果您尝试使用EntityManagers删除方法删除项目并只是传递当前选定的项目,则该对象将被分离。所以,我所做的是从数据库中检索项目,将其放入持久化上下文并删除它。我原本以为它有点不同,无需从数据库中检索对象,再次将其放入上下文并将其删除。是否有可能避免这些额外的步骤?
不。 EntityManager#remove()
需要托管实体。即它必须在同一笔交易中获得。当多个用户同时编辑甚至删除实体时,这将会遇到麻烦,包括那些需要级联的所有FK关系(如果有的话)。规范的方式是:
public void deleteItem(Item item) {
entityManager.remove(entityManager.contains(item) ? item : entityManager.merge(item));
}
我已经多次尝试更新现有项目。如果您询问持久性上下文是否已使用EntityManagers contains方法管理已修改的项目,则会发现该项目不在持久性上下文中。所以,再一次,我需要从数据库中检索对象,然后使用merge更新它。我真的怀疑这是首选方式,如果有人对此有任何暗示,我会很高兴。
我不确定你的具体问题是什么。这应该工作。如果您从DB获取该项目,然后像当前那样合并它,那么您将错过所有更改的值。实际上,你正在做一个无操作。以下应该只是工作:
public Item updateItem(Item item) {
return entityManager.merge(item);
}
我假设整个事务仍处于“打开”状态,并且对象仍保留在持久化上下文中。
不。只要EJB的客户端(在您的情况下是JSF托管bean)对其进行单个方法调用,事务就会持续@Stateless
EJB。请注意,默认情况下,无论EJB方法中的任何嵌套EJB方法调用如何,它们都将在同一事务中运行。因此,一旦顶级@Stateless
EJB方法调用返回,事务将被提交并且实体将被分离。当多个用户同时编辑同一个实体时,您可能会在所有地方遇到OptimisticLockException
。