jpa 异常捕获_JPA和CMT –为什么捕获持久性异常不够?

jpa 异常捕获

使用CMT( 容器管理的事务 )进入EJB和JPA的世界非常舒适。 只需定义一些注释来划分事务边界(或使用默认值)即可,仅此而已–无需摆弄手动开始,提交或回滚操作。 回滚事务的一种方法是从EJB的业务方法中引发非应用程序异常(或具有rollback = true的应用程序异常)。 看起来很简单:如果在某些操作过程中可能会引发异常,并且您不想回滚tx,那么您应该捕获该异常就可以了。 现在,您可以在同一仍处于活动状态的事务中再次重试该易失性操作。

现在,对于从用户组件抛出的应用程序异常,这一切都是正确 。 问题是– 除了其他组件引发的异常之外,还有什么? 就像JPA的EntityManager抛出PersistenceException ? 这就是故事的开始。

我们想要实现的目标

设想以下情形:您有一个名为E的实体。它包含:

  • id –这是主键,
  • 名称 -这是一些易于理解的实体名称,
  • 内容 –包含字符串的任意字段–它模拟“高级属性”,例如,在持久性/合并期间进行计算会导致错误。
  • 代码 –包含OK或ERROR字符串–定义高级属性是否成功持久保存,

您要持久化E。您假定E的基本属性将始终被成功持久化。 但是,高级属性需要一些额外的计算或操作,这可能会导致例如从数据库引发约束冲突。 如果发生这种情况,您仍然希望E保留在数据库中(但仅填充基本属性,并且将代码属性设置为“ ERROR”)。

换句话说,这是您可能想到的:

  1. 坚持E的基本属性,
  2. 尝试使用脆弱的高级属性对其进行更新,
  3. 如果从步骤2抛出了PersistenceException捕获它,将'code'属性设置为“ ERROR”并清除所有高级属性(它们导致异常),
  4. 更新E。

天真的解决方案

转到EJB的代码,这就是您可以尝试执行的方式(假设使用默认的TransactionAttributes):

public void mergeEntity() {
    MyEntity entity = new MyEntity('entityName', 'OK', 'DEFAULT');

    em.persist(entity);

    // This will raise DB constraint violation
    entity.setContent('tooLongContentValue');

    // We don't need em.merge(entity) - our entity is in managed mode.

    try {
        em.flush();  // Force the flushing to occur now, not during method commit.
    } catch (PersistenceException e) {  
        // Clear the properties to be able to persist the entity.
        entity.setContent('');
        entity.setCode('ERROR');

       // We don't need em.merge(entity) - our entity is in managed mode.
    }
}

这个例子有什么问题?

捕获由EntityManager抛出的PersistenceException 不会阻止事务回滚 。 并不是在EJB中不缓存异常将tx标记为回滚。 这是EntityManager 抛出的非应用程序异常 ,将tx标记为回滚。 更不用说资源本身可能会在内部将tx标记为回滚。 这实际上意味着您的应用程序实际上无法控制此类tx行为。 此外,由于事务回滚,我们的实体已移至分离状态。 因此,在此方法末尾需要一些em.merge(entity)

工作方案

那么如何处理这种自动事务回滚? 因为我们正在使用CMT,所以唯一的方法是定义另一种业务方法,该方法将启动新事务并在那里执行所有易碎的操作 。 这样,即使将抛出(并捕获) PersistenceException ,它也将仅标记要回滚的新事务。 我们的主要TX将保持不变。 在下面,您可以从此处看到一些代码示例(为简洁起见,删除了日志记录语句):

public void mergeEntity() {
    MyEntity entity = new MyEntity('entityName', 'OK', 'DEFAULT');

    em.persist(entity);

    try {
        self.tryMergingEntity(entity);
    } catch (UpdateException ex) {
        entity.setContent('');
        entity.setCode('ERROR');
    }
}

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void tryMergingEntity(final MyEntity entity) throws UpdateException {
    entity.setContent('tooLongContentValue');

    em.merge(entity);

    try {
        em.flush();
    } catch (PersistenceException e) {
        throw new UpdateException();
    }
}
注意:
  • UpdateException@ApplicationException ,它扩展了Exception(因此默认情况下为rollback=false )。 用于通知更新操作失败。 或者,您可以更改tryMergingEntity(-)方法签名以返回布尔值而不是void。 此布尔值可以描述更新是否成功。
  • self是对我们自己的EJB的自我引用。 这是使用EJB容器代理的必需步骤,该代理使被调用方法的@TransactionAttribute起作用。 或者,您可以使用SessionContext#getBusinessObject(clazz).tryMergingEntity(entity)
  • em.merge(entity)是至关重要的。 我们正在tryMergingEntity(-)中开始新事务,因此该实体不在持久性上下文中。
  • 此方法不需要任何其他合并或刷新。 tx尚未回滚,因此批准了CMT的常规功能,这意味着在tx提交期间将自动刷新对实体的所有更改。

让我们再次强调最重要的一点: 如果您捕获到异常,并不表示您当前的事务没有被标记为回滚。 PersistenceException不是ApplicationException,即使您是否捕获它,也将使您的tx回滚。

JTA BMT解决方案

我们一直在谈论CMT。 JTA BMT呢? 好吧,作为一个好处,找到下面的代码,该代码显示如何使用BMT处理此问题(也可在此处访问):

public void mergeEntity() throws Exception {
    utx.begin();
    MyEntity entity = new MyEntity('entityName', 'OK', 'DEFAULT');
    em.persist(entity);
    utx.commit();

    utx.begin();
    entity.setContent('tooLongContentValue');

    em.merge(entity);

    try {
        em.flush();
    } catch (PersistenceException e) {
        utx.rollback();

        utx.begin();
        entity.setContent('');
        entity.setCode('ERROR');

        em.merge(entity);
        utx.commit();
    }
}

使用JTA BMT,我们可以用一种方法来完成所有这一切。 这是因为我们控制着tx何时开始以及提交/回滚 (看看那些utx.begin()/ commit()/ rollback()。尽管如此,结果还是一样的–抛出PersistenceException我们的tx被标记为回滚然后您可以使用UserTransaction#getStatus()进行检查,并将其与诸如Status.STATUS_MARKED_ROLLBACK之类的常量进行比较,并可以在我的GitHub帐户上检查整个代码。

参考: JPA和CMT –为什么捕获持久性异常不足? 从我们的JCG合作伙伴 Piotr Nowicki在Piotr Nowicki的首页博客中获得。

翻译自: https://www.javacodegeeks.com/2013/03/jpa-and-cmt-why-catching-persistence-exception-is-not-enough.html

jpa 异常捕获

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值