【问题标题】:Throwing an application exception causes TransactionalException抛出应用程序异常会导致 TransactionalException
【发布时间】:2014-02-17 06:17:45
【问题描述】:

我正在实现一个 JEE7 Web 应用程序。在我的工作中,我发现处理我的自定义异常有问题。

我编辑了我帐户的属性,使其具有非唯一的登录字段。然后我调用AccountEditBean#editAccount() 来运行编辑过程。当进程来到AccountFacade#edit() 时,我可以看到(在调试中)PersistenceException 被捕获并且我的自定义NonUniqueException 被抛出。问题是,异常没有传播到外观类之外,也没有在AccountEditBean 中处理。而不是TransactionalException 出现在throw 之后:

WARNING:   EJB5184:A system exception occurred during an invocation on
EJB ADMEndpoint, method: public void
pl.rozart.greatidea.adm.endpoints.ADMEndpoint.editAccount(pl.rozart.greatidea.entities.Account)
throws pl.rozart.greatidea.exceptions.BaseApplicationException
WARNING:   javax.transaction.TransactionalException: Managed bean with
Transactional annotation and TxType of REQUIRES_NEW encountered
exception during commit javax.transaction.RollbackException:
Transaction marked for rollback.

附加信息: NonUniqueException 扩展 BaseApplicationException ,标记为 @ApplicationException(rollback=true)

这是编辑过程的代码:

AccountEditBean:

@Named(value = "accountEditBean")
@ViewScoped
public class AccountEditBean extends UtilityBean implements Serializable {

    @Inject
    ADMEndpointLocal admEndpoint;

    private Account account;

    public void editAccount() {
        try {
            admEndpoint.editAccount(this.account);
            Messages.addInfo(ACCOUNT_DETAILS_FORM, KEY_CHANGES_SUCCESS);
        } catch (NonUniqueException e) {
            Messages.addError(ACCOUNT_DETAILS_FORM, e.getMessage());
        } catch (BaseApplicationException e) {
            Messages.addFatal(ACCOUNT_DETAILS_FORM, e.getMessage());
        }
    }

}

ADM端点:

@Stateful
@Transactional(Transactional.TxType.REQUIRES_NEW)
@TransactionTracker
public class ADMEndpoint extends LoggingStateBean implements ADMEndpointLocal, SessionSynchronization {

    @EJB(name = "ADMAccountFacade")
    private AccountFacadeLocal accountFacade;

    private Account account;

    @Override
    public void editAccount(Account account) throws BaseApplicationException {
        this.account.setLogin(account.getLogin());
        this.account.setEmail(account.getEmail());
        accountFacade.edit(this.account);
    }

}

ADMAccountFacade:

@Stateless(name = "ADMAccountFacade")
@Transactional(Transactional.TxType.MANDATORY)
@TransactionTracker
public class AccountFacade extends AbstractFacade<Account> implements AccountFacadeLocal {

    @PersistenceContext(unitName = "myPU")
    private EntityManager em;

    @Override
    public void edit(Account account) throws BaseApplicationException {
        try {
            em.merge(account);
            em.flush();
        } catch (PersistenceException e){
            if(e.getMessage().contains(Account.CONSTRAINT_ACCOUNT_LOGIN_UNIQUE)){
                throw new NonUniqueException(NonUniqueException.MSG_NON_UNIQUE_ACCOUNT_LOGIN, e);
            }else{
                throw new BaseDatabaseException(BaseDatabaseException.MSG_GENERAL_DATABASE_ERROR, e);
            }
        }
    }
}

您知道问题的原因是什么吗?它出现在我的每一个立面中,除了所有自定义例外。

【问题讨论】:

  • ADMEndpoint类的editAccount方法中没有account字段怎么调用accountFacade.edit(this.account)?
  • 对不起,我只是没有把它贴在这里。现在应该很清楚了。 :)
  • 那么Account参数在editAccount方法中是没有用的,因为你从来没有使用过!
  • 我希望现在一切都清楚了。 :)
  • 我认为您应该将 @Transactional 更改为 @TransactionAttribute,因为带有注释的 EJB。@Transactional 在 java 7 中的 managedbean 上而不是在 EJB 中...

标签: java jakarta-ee jpa exception-handling transactions


【解决方案1】:

您正在从一个方法中抛出一个异常,该方法的调用将在运行时被拦截,并在其周围附加额外的逻辑:

  • 事务管理;
  • 异常处理。

您的异常不能透明地跳过该逻辑,并且规范(可能)说将抛出 TransactionalException,包装您的原始异常(再次,可能---我不是那么亲密详细信息)。

【讨论】:

  • 奇怪的是我在实现 JEE6 应用程序时做的完全一样(当然我使用@TransactionAttribute 而不是@Transactional)并且我的自定义异常被正确传播。
  • 这种变化实际上可能是一件好事:EJB 的一个常见缺陷是无法反序列化异常,从而导致序列化异常掩盖了原始问题。
【解决方案2】:

我认为您应该将 @Transactional 更改为 @TransactionAttribute,因为 EJB 带有注释。 @Transactional 放在 java 7 中的 managedbean 上,而不是 EJB 中......

我在这里复制了我的评论,因为我没有足够的积分可以浪费:)

【讨论】:

  • 谢谢它确实解决了问题。但不就是这样吗:@Transactional注解是给CDI bean的,在JEE7中bean可以同时是EJB和CDI?
  • 是的,他们可以。 managedbeans=cdi 托管 bean。我只是简单地写了下来。正如我上面提到的(据我所知),当我们尝试在远程调用中访问它时,EJB 可以同时是 CDI bean。所以我们使用@inject 而不是@EJB 但它不涉及事务处理。
猜你喜欢
  • 2016-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 2014-11-25
  • 2014-10-26
  • 2017-05-03
  • 1970-01-01
相关资源
最近更新 更多