容器管理事务中CRUD操作中实体的行为

时间:2015-01-06 17:11:17

标签: jsf jpa ejb

该场景基于完整的Java EE 7(Wildfly 8)并使用EJB 3.2,JPA 2.1,JSF 2.2(Mojarra,Primefaces库)和以下项目结构:

  • 网络项目
  • EAR项目
  • EJB项目
  • JPA项目

基本的CRUD场景如下:

  • 用户进入页面或点击新建,输入项目名称并点击保存> item保存在数据库中并添加到表
  • 用户从表格中选择项目>项目名称字段使用当前所选项目
  • 中的适当值进行更新
  • 用户从表格中选择项目,修改它并点击保存>修改后的项目在数据库和表格中更新
  • 用户从表格中选择项目,然后点击删除> 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());
}

}

支持bean是@ViewScoped。后面的EJB是无状态的,我想它是一个容器管理的bean(因为它是默认设置)。

如果您尝试使用EntityManager s remove方法删除项目并只是传递当前选定的项目,则会分离该对象。所以,我所做的是从数据库中检索项目,将其放入持久化上下文并删除它。我原本以为它有点不同,无需从数据库中检索对象,再次将其放入上下文并将其删除。是否有可能避免这些额外的步骤?

我已多次尝试更新现有项目。如果您询问持久化上下文是否已使用EntityManager contains方法管理已修改的项目,则事实证明该项目不在持久性上下文中。因此,我需要再次从数据库中检索对象,然后使用merge更新它。我真的怀疑这是首选的方式,如果有人对此有任何暗示,我会很高兴。

我假设整个交易仍然是&#34;开放&#34;并且object / s保持/ s附加到持久化上下文。

谢谢。

1 个答案:

答案 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

另见: