具有扩展持久化上下文的意外分离实体

时间:2012-10-01 14:50:53

标签: java jboss resteasy entitymanager

我试图通过使用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.
}

使用hibernate存储(简化)状态。它由三个类组成(父级和两个子级,其中一个包含子级列表):

@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被访问之后抛出任何异常)呼叫)。当我不希望分离扩展持久化上下文中的实体时,是否存在可接受的处理异常的策略?

1 个答案:

答案 0 :(得分:1)

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

  

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

因此,当事务被回滚时,并且通过调用sessionBean来分离活动事务中涉及的实体,我将其置于事务中。

解决这个问题的方法似乎是使用@AppicationException注释修饰可接受的异常。这将异常标记为非致命异常并阻止事务回滚。 David Blevin详细描述了这种方法。