【问题标题】:Behaviour of entities in CRUD operations in container-managed transactions容器管理事务中 CRUD 操作中实体的行为
【发布时间】:2015-03-04 10:00:17
【问题描述】:

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

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

基本的 CRUD 场景如下所示:

  • 用户进入页面或点击新建,输入项目名称并点击保存>项目保存在数据库中并添加到表中
  • 用户从表中选择项目 > 项目名称字段已更新为当前所选项目中的适当值
  • 用户从表中选择项目,修改它并点击保存 > 修改的项目在数据库和表中更新
  • 用户从表中选择项目并单击删除 > 从数据库和表中删除项目

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(因为它是默认设置)。

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

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

我假设整个事务保持“打开”并且对象保持/s 附加到持久性上下文。

谢谢。

【问题讨论】:

    标签: jsf jpa ejb


    【解决方案1】:

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

    不。 EntityManager#remove() 需要一个托管实体。 IE。它必须在同一交易中获得。否则,当多个用户同时编辑甚至删除实体时,这将遇到麻烦,包括所有需要级联的 FK 关系(如果有的话)。规范的方式是:

    public void deleteItem(Item item) {
        entityManager.remove(entityManager.contains(item) ? item : entityManager.merge(item));
    }
    

    我曾多次尝试更新已存在的项目。如果您询问持久性上下文是否已使用 EntityManagers contains 方法管理修改的项目,结果表明该项目不在持久性上下文中。因此,我需要再一次从数据库中检索对象,然后使用合并对其进行更新。我真的怀疑这是首选方式,如果有人对此有任何提示,我会很高兴。

    我不确定您的具体问题是什么。这应该只是工作。如果您从 DB 获取项目然后合并它,就像您目前所做的那样,那么您将错过所有更改的值。实际上,您正在执行无操作。以下应该可以正常工作:

    public Item updateItem(Item item) { 
        return entityManager.merge(item);
    }
    

    我假设整个事务保持“打开”并且对象保持/s 附加到持久性上下文。

    不。在@Stateless EJB 的情况下,只要 EJB 的客户端(在您的情况下为 JSF 托管 bean)对其进行单个方法调用,事务就会持续。请注意,默认情况下,无论 EJB 方法中的任何嵌套 EJB 方法调用如何,它们都将在同一个事务中运行。因此,一旦顶级@Stateless EJB 方法调用返回,事务将被提交并且实体变得分离。当多个用户同时编辑同一个实体时,您可能会在所有地方遇到OptimisticLockExceptions。

    另见:

    【讨论】:

    • 感谢您为我解决这个问题。好吧,我花了很长时间才弄清楚所有这些并重建。我可以确认entityManager.merge(item); 只是按照描述的方式工作。从数据库中获取实体并在之后合并它确实是一个无用的(无)操作。
    猜你喜欢
    • 2015-12-12
    • 1970-01-01
    • 1970-01-01
    • 2015-02-18
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    • 1970-01-01
    • 2018-09-14
    相关资源
    最近更新 更多