我试图通过使用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;
}
}
}
要重现此问题,请按以下顺序执行:
em.persist(child)
上出现分离的实体错误。我不明白的是我是如何/为什么我最终会使用分离的实体。在实际代码中,我将在修改状态之前执行一些请求/状态验证(因此我没有理由看到状态已被分离)。
如果我删除了对sessionBean.getLock()
的调用,则问题就会消失(对象会保持正确)。锁定方法的目的本质上是序列化对会话状态的访问,但是当前getLock()
方法是空的,感觉问题可能与我在抛出之前调用有状态bean这一事实有关例外。
任何人都可以解释发生了什么导致我的实体变得脱离/如果有办法避免它(并且理想情况下指向任何支持解释的文档)?
虽然我可能有办法解决当前问题,在访问有状态bean之前执行验证,但我关注的是一般情况(在有状态bean被访问之后抛出任何异常)呼叫)。当我不希望分离扩展持久化上下文中的实体时,是否存在可接受的处理异常的策略?
答案 0 :(得分:1)
看起来这是预期的行为。感谢Scott Marlow对JPA规范的引用,第3.3.2节。
交易回滚 对于事务范围和扩展 持久化上下文,事务回滚导致所有预先存在 托管实例和删除实例[31]变得分离。该 实例的状态将是实例的状态 该事务被回滚。通常是事务回滚 导致持久化上下文处于不一致状态 回滚点。特别是版本属性和状态 生成的状态(例如,生成的主键)可能不一致。 以前由持久性上下文管理的实例 (包括那些持久的新实例 因此,交易)可能无法以与其他方式相同的方式重复使用 分离的对象 - 例如,它们可能在传递给合并时失败 操作。[32]
因此,当事务被回滚时,并且通过调用sessionBean来分离活动事务中涉及的实体,我将其置于事务中。
解决这个问题的方法似乎是使用@AppicationException
注释修饰可接受的异常。这将异常标记为非致命异常并阻止事务回滚。 David Blevin详细描述了这种方法。