3

我试图通过使用 EXTENDED_PERSISTENT_CONTEXT 来维护多个调用的状态。我的理解是托管实体不会在调用之间分离,但是在我之前抛出验证错误之后,我不断收到与调用中分离实体相关的错误。状态在有状态会话 bean 中维护:

@Named(SessionFacadeBean.SEAM_NAME)
@SessionScoped
@Stateful
@LocalBean
@AccessTimeout(value = 10, unit = TimeUnit.SECONDS)
public class SessionFacadeBean implements Serializable
{
    public static final String  SEAM_NAME        = "sessionCacheBean";

    @PersistenceContext(unitName = GlobalParameters.BACKEND_CODE_PERSISTENCE_CONTEXT_NAME, type = PersistenceContextType.EXTENDED)
    private EntityManager       em;

    private ParentOne sessionData;

    public synchronized ParentOne getSessionData() {
        if(sessionData == null) {
            sessionData = new ChildTwo();
        }
        return sessionData;
    }

    public boolean getLock() {
        return true;
    }

    public void clearLock() {
    }

    // Other stuff I don’t ‘think’ is relevant.
}

(简化的)状态正在使用休眠进行存储。它由三个类(一个父类和两个子类,其中一个包含子类列表)组成:

@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "Class", length = 50)
@Entity
public class ParentOne 
{   
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @XmlElement(name = "ID")
    private Long              iD;

    @XmlElement(name = "name")
    protected String              friendlyName          = "";
}


@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Entity
public class ChildOne extends ParentOne
{
    public ChildOne(String name, ParentOne child) {
        super(name);
        myChild = child;
    }

    @ManyToOne(cascade = CascadeType.ALL)
    protected ParentOne myChild;   
}


@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Entity
public class ChildTwo extends ParentOne
{
    public ChildTwo() {
            super(“common”);
    }
}

我正在从无状态 bean 访问有状态 bean,如下所示:

@Stateless
@LocalBean
@Path("/")
public class MyService
{
    @PersistenceContext(unitName = GlobalParameters.BACKEND_CODE_PERSISTENCE_CONTEXT_NAME)
    private EntityManager       em;

    @Inject
    private SessionFacadeBean   sessionBean;

    @POST
    @Path("/create/item")
    @ValidateRequest
    public ComponentShortSummary addItem(@Form NewItemForm itemForm)
    {       
        if(sessionBean.getLock()) {
            try {
                if(itemForm.getName().equals("INVALID") == true) {
                    throw new ConstraintViolationException("Failed", new HashSet<ConstraintViolation<?>>());
                }

                ChildOne child = new ChildOne(itemForm.getName(), sessionBean.getSessionData());
                em.persist(child);
                return null;
            }
            finally {
                sessionBean.clearLock();
            }
        } else {
            return null;
        }
    }
}

为了重现该问题,我执行以下顺序:

  • 使用有效名称调用 addItem(这会将项目保存到数据库中)。
  • 使用名称“INVALID”调用 addItem,这会引发约束异常。
  • 使用有效名称调用 addItem(这会导致在em.persist(child).

我不明白的是我如何/为什么最终得到分离的实体。在实际代码中,我会在修改状态之前执行一些请求/状态验证(因此我没有理由看到状态已被分离)。

如果我删除对的调用,sessionBean.getLock()那么问题就会消失(对象正确存在)。锁定方法的目的本质上是序列化对会话状态的访问,但是目前该getLock()方法是空的,感觉问题可能与我在抛出异常之前调用有状态 bean 的事实有关。

任何人都可以解释导致我的实体变得分离的原因/如果有办法避免它(最好将我指向任何支持解释的文档)?

虽然可能有一些方法可以解决当前问题,在访问有状态 bean 之前执行验证,但我担心一般情况(在调用中访问有状态 bean 后抛出任何异常)。当我不希望扩展持久上下文中的实体被分离时,是否有一种公认的处理异常的策略?

4

1 回答 1

2

看起来这是预期的行为。感谢Scott Marlow对 JPA 规范第 3.3.2 节的引用。

事务回滚 对于事务范围和扩展的持久性上下文,事务回滚会导致所有预先存在的托管实例和已删除的实例[31] 分离。实例的状态将是事务回滚时实例的状态。事务回滚通常会导致持久性上下文在回滚点处于不一致状态。特别地,版本属性的状态和生成的状态(例如,生成的主键)可能不一致。因此,以前由持久性上下文管理的实例(包括在该事务中持久化的新实例)可能无法以与其他分离对象相同的方式重用——例如,它们在传递给合并操作时可能会失败。 [32 ]

So, entities that are involved in the active transaction are detached when the transaction is rolled back and by calling out to the sessionBean I am involving it in the transaction.

One way around this appears to be to decorate acceptable exceptions with the @AppicationException annotation. This marks the exception as non-fatal and prevents the transaction from being rolled back. This approach is described in some detail by David Blevin.

于 2012-10-09T19:54:07.247 回答