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



我正试图通过使用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之后会抛出任何异常)。当我不希望从扩展的持久上下文中分离实体时,是否有一种可接受的策略来处理异常?

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

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

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

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

最新更新