我有这个类,在持久性异常(在其他地方处理)的情况下,我采用了三种方法来处理分离的实体状态:
@ManagedBean
@ViewScoped
public class EntityBean implements Serializable
{
@EJB
private PersistenceService service;
private Document entity;
public void update()
{
// HANDLING 1. ignore errors
service.transact(em ->
{
entity = em.merge(entity);
// some other code that modifies [entity] properties:
// entity.setCode(...);
// entity.setResposible(...);
// entity.setSecurityLevel(...);
}); // an exception may be thrown on method return (rollback),
// but [entity] has already been reassigned with a "dirty" one.
//------------------------------------------------------------------
// HANDLING 2. ensure entity is untouched before flush is ok
service.transact(em ->
{
Document managed = em.merge(entity);
// some other code that modifies [managed] properties:
// managed.setCode(...);
// managed.setResposible(...);
// managed.setSecurityLevel(...);
em.flush(); // an exception may be thrown here (rollback)
// forcing method exit without [entity] being reassigned.
entity = managed;
}); // an exception may be thrown on method return (rollback),
// but [entity] has already been reassigned with a "dirty" one.
//------------------------------------------------------------------
// HANDLING 3. ensure entity is untouched before whole transaction is ok
AtomicReference<Document> reference = new AtomicReference<>();
service.transact(em ->
{
Document managed = em.merge(entity);
// some other code that modifies [managed] properties:
// managed.setCode(...);
// managed.setResposible(...);
// managed.setSecurityLevel(...);
reference.set(managed);
}); // an exception may be thrown on method return (rollback),
// and [entity] is safe, it's not been reassigned yet.
entity = reference.get();
}
...
}
PersistenceService#transact(Consumer<EntityManager> consumer)
可以抛出未经检查的异常。
目标是保持实体的状态与数据库的状态保持一致,即使在异常的情况下(防止实体在事务失败后变为“脏”)。
方法1.显然很幼稚,并不能保证连贯性。
方法2.断言冲洗后没有任何问题。
方法3.如果整个交易中存在例外,则阻止新实体分配
的问题:
flush
[已排除]和commit
[包含]之间发生异常的情况?谢谢
请注意,我已经能够回滚事务并关闭EntityManager(PersistenceService#transact
将优雅地执行它),但我需要解决数据库状态,并且业务对象确实不同步。通常这不是问题。在我的情况下,这是 问题,因为异常通常由BeanValidator
生成(JPA方面,而不是JSF方面,计算值取决于用户输入)我希望用户输入正确的值并重试,而不会丢失他之前输入的值。
旁注:我正在使用Hibernate 5.2.1
这是PersistenceService(CMT)
@Stateless
@Local
public class PersistenceService implements Serializable
{
@PersistenceContext
private EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void transact(Consumer<EntityManager> consumer)
{
consumer.accept(em);
}
}
就是这样!第1点和第2点的重要解释。
我只是希望你在第3点详细说明一点,并就真实用例提供一些建议。
但是,我肯定不会使用AtomicReference或类似的繁琐构造。 Java EE,Spring和其他框架和应用程序容器支持通过注释声明事务方法:只需使用事务方法返回的结果。
当您必须修改单个实体时,事务方法只会将分离的实体作为参数并返回更新的实体,这很容易。
public Document updateDocument(Document doc)
{
Document managed = em.merge(doc);
// managed.setXxx(...);
// managed.setYyy(...);
return managed;
}
但是当您需要在单个事务中修改多个时,该方法可能会变得非常痛苦:
public LinkTicketResult linkTicket(Node node, Ticket ticket)
{
LinkTicketResult result = new LinkTicketResult();
Node managedNode = em.merge(node);
result.setNode(managedNode);
// modify managedNode
Ticket managedTicket = em.merge(ticket);
result.setTicket(managedTicket);
// modify managedTicket
Remark managedRemark = createRemark(...);
result.setRemark(managedemark);
return result;
}
在这种情况下,我的痛苦:
@EJB
)LinkTicketResult
LinkTicketParameters
)可能我没有看到一些在我面前的大事,如果你能指出我正确的方向,我将非常感激。
答案 0 :(得分:3)
方法3.是否比方法2更安全。?
是。它不仅更安全(参见第2点),而且在概念上更正确,因为只有当您证明相关事务已成功时才更改与事务相关的状态。
是否存在在flush [excluded]和commit [included]之间抛出异常的情况?
是。例如:
乐观地假设交易不会遭遇争用 对于实体。 将在交易附近验证实体版本 端。强>
在单个事务中的每次刷新操作期间检查optimistick锁定违规将既不高效也不实用。
延迟完整性约束(在db中的提交时强制执行)。不经常使用,但是这种情况的说明性示例。
稍后维护和重构。您或其他人可能会在最后一次显式调用flush之后引入其他更改。
有没有一种标准方法可以解决这个常见问题?
是的,我会说你的第三种方法是标准方法:使用完整而成功的交易结果。
但是,我肯定不会使用AtomicReference
或类似的繁琐构造。 Java EE,Spring和其他框架和应用程序容器支持通过注释声明事务方法:只需使用从事务方法返回的结果。
答案 1 :(得分:0)
不确定这是否完全达到了这一点,但在异常之后只有一种方法可以恢复:回滚并关闭EM。来自https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/transactions.html#transactions-basics-issues
实体管理器抛出的异常意味着您必须回滚 您的数据库事务并立即关闭EntityManager (稍后将详细讨论)。如果您的EntityManager绑定 在应用程序中,您必须停止该应用程序。回滚 数据库事务并没有将您的业务对象重新放入 说他们在交易的开始。这意味着 数据库状态和业务对象确实不同步。平时 这不是问题,因为例外是不可恢复的 无论如何,必须在回滚后重新开始你的工作单元。
- 编辑 - 另请参阅http://piotrnowicki.com/2013/03/jpa-and-cmt-why-catching-persistence-exception-is-not-enough/
ps:downvote不是我的。