我有这个类,在持久性异常(在其他地方处理)的情况下,我采用三种方法来处理分离的实体状态:
@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 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.如果整个事务中存在异常,则阻止新实体分配
问题:
>方法3是否比方法2更安全?
>是否存在flush [excluded]和commit [included]之间抛出异常的情况?
>有没有一种标准的方法来处理这个常见问题?
谢谢
请注意,我已经能够回滚事务并关闭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 consumer)
{
consumer.accept(em);
}
}
@DraganBozanovic
而已!第1点和第2点的重要解释.
我只是爱你在第3点详细说明一点,并给我一些关于实际用例的建议.
However, I would definitely not use AtomicReference or similar cumbersome constructs. Java EE, Spring and other frameworks and application containers support declaring transactional methods via annotations: Simply use the result returned from a transactional method.
当您必须修改单个实体时,事务方法只会将分离的实体作为参数并返回更新的实体,这很容易.
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)
>在大多数情况下,JSF控制器操作只调用EJB方法,从返回的容器中提取更新的实体并将它们重新分配给本地字段
>我的代码将被“一次性投手”逐渐污染,对我来说太多了.
可能我没有看到像我这样的大事,如果你能指出我正确的方向,我将非常感激.