【问题标题】:JPA: How to use transactions with JTA EntityManager?JPA:如何在 JTA EntityManager 中使用事务?
【发布时间】:2012-06-28 19:08:15
【问题描述】:

首先,this 解决方案不适合我,因为我无法更改持久性单元。

我的问题是我使用了一个 JTA EntityManager,但我只需要一个用例,比如事务:

public boolean saveWithResult(PointsValidityPeriod pointsValidityPeriod)
{
    //TODO use transaction here 
    super.save(pointsValidityPeriod);

    if (updatePrevious(pointsValidityPeriod.getValidFrom()) != 1)
    {
        logger.error("Update of Period was not possible, because UPDATE returned no single result.");

        return false;
    }

    pointsValidityPeriodEvent.fire(pointsValidityPeriod);

    return true;
}

保存方法(我无法更改):

public void save(T entity)
{
    getEntityManager().persist(entity);
}

你看,有一个保存调用,但是如果更新出错,这个保存必须回滚,那么我该如何实现呢?有什么想法吗?

【问题讨论】:

  • JTA 的全部意义在于管理事务。您在哪个环境中执行您的应用程序?具有 EJB 的应用程序服务器?然后使用 EJB 来划分事务:这是他们的重点。
  • 默认情况下,EJB 是事务性的。默认情况下,EJB 方法中的所有代码都在事务中执行。
  • 我更改了方法调用。如果我在 saveWithResult 方法中说if (updatePrevious() == 2) { => don't invoke save.},那么 updatePrevious 将不会回滚,所以 UPDATE 已经执行但 INSERT (save) 没有,那么这是事务性的吗?我不明白。

标签: java transactions jpa-2.0 entitymanager jta


【解决方案1】:

这完全取决于您的方法事务注释。如果您没有,则默认情况下事务边界设置为 JB 所指出的业务方法。

所以,整个方法是一个事务。通常,save 方法的事务管理会很重要(如果是 REQUIREDREQUIRES_NEW,则不同),但在这种情况下,您正在进行本地方法调用 - 不是业务方法调用,所以任何save 方法的事务管理设置不适用于此处。

因此,如果 update 失败,您可以基于自动事务回滚(如果它是一个 EntityManager 调用,它会生成回滚 Tx 的异常),或者您可以手动注入 SessionContext 并在其上调用 setRollbackOnly(),一些字里行间:

@Resource
SessionContext ctx;

public boolean saveWithResult(PointsValidityPeriod pointsValidityPeriod)
{
   // ...
    if (updatePrevious(pointsValidityPeriod.getValidFrom()) != 1)
    {
        // ...
        ctx.setRollbackOnly();
        // ...
    }
}

【讨论】:

  • 不幸的是,我不能这样做。 Eclipse 向我显示“没有 bean 有资格注入到注入点 [JSR-299 §5.2.1]”,最后它在启动 JBoss 时导致异常:org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied在注入点 [[field] @Inject 带有限定符 [@Default] 的类型 [SessionContext] 的依赖项
  • 注解不应该是@Resource吗?请参阅javahowto.blogspot.fr/2006/06/… 了解如何从 EJB 获取 SessionContext。
  • 啊,当然是 JB - @Resource!抱歉,CDI 太多让我成为一个迟钝的男孩!
【解决方案2】:

我有解决方案,但我不知道为什么会这样。也许有人可以向我解释。 在我的支持 bean 中有 save 方法。有2个实体。 getEntity() 是支持 bean 使用的当前实体,currentValidityPeriod 是该实体的另一个实例,它持有该实体的最新数据库状态,由 currentValidityPeriod = pointsValidityService.findCurrent() 获取; findCurrent() 只是一个执行 TypedQuery 的方法:

public PointsValidityPeriod findCurrent()
{
    TypedQuery<PointsValidityPeriod> validityPeriodQuery = entityManager.createNamedQuery(PointsValidityPeriod.FindCurrent, PointsValidityPeriod.class);

    return validityPeriodQuery.getSingleResult();
}

这是视图调用的保存方法。如您所见,我只将当前实体传递给服务保存方法。在之前的Entity(currentValidityPeriod)中,我设置了另一个值。

@Override
public void save()
{       
    Date validFrom = new DateTime(DateTimeZone.forID("Europe/Vienna")).toDate();
    getEntity().setValidFrom(validFrom);

    currentValidityPeriod.setValidThru(validFrom);

    pointsValidityService.save(getEntity());

    addSavedSuccessfullyMessage();
}

这是将被调用的服务方法:

@Override
public void save(PointsValidityPeriod pointsValidityPeriod)
{
    super.save(pointsValidityPeriod);
}

...和超类的保存方法:

public void save(T entity)
{
    getEntityManager().persist(entity);
}

为什么实体管理器会保留新实体并更新旧实体?我只是将一个实体传递给实体管理器。虽然这是预期的行为,但我想知道为什么会这样。

【讨论】:

  • 当您在事务中时,JPA 提供者需要在事务提交时将处于“托管”状态的实体的状态与数据库同步。所以换句话说 - 如果您在事务中从数据库中获取实体并更改其状态,则在 tx 提交期间它将自动在此实体上执行 update
猜你喜欢
  • 2011-06-30
  • 1970-01-01
  • 1970-01-01
  • 2017-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多