CDI + EJB 3 + EJB事务

时间:2014-04-16 21:05:03

标签: ejb cdi

我需要审核ejb bean的调用。说审计我的意思是写信息,例如当前记录的用户,方法名称,对数据库的附加描述。我决定使用CDI装饰器来做到这一点:

@Decorator
public class AccountServiceBeanDecorator implements AccountService {

  @Inject
  @Delegate
  @Any
  AccountService accountService;

  @EJB
  private AuditService auditService;

  @Override
  public Account createAccount(Account account) {
    auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
    return accountService.createAccount(account);
  }

}

和装饰类:

@Stateless
public class AccountServiceBean implements AccountService {

   @Override
   public Account createAccount(Account account) {
     ... 
   }
}

现在,如果我从另一个ejb无状态bean调用AccountService,那么事务会发生什么?:

@Stateless
public ApplicationFacadeBean implements ApplicationFacade {

  @EJB
  private AccountService accountService;

  @Override
  public Account createAccount(Account account) {
    return accountService.createAccount(account);
  }

}

我想在decorator(AccountServiceBeanDecorator)和装饰类(AccountServiceBean)中记录事务状态,所以我在两个类中都将TransactionSynchronizationRegistry注入资源:

@Decorator
public class AccountServiceBeanDecorator implements AccountService {

  @Inject
  @Delegate
  @Any
  AccountService accountService;

  @EJB
  private AuditService auditService;

  @Resource
  private TransactionSynchronizationRegistry reg;

  @Override
  public Account createAccount(Account account) {
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
    return accountService.createAccount(account);
  }

}

@Stateless
public class AccountServiceBean implements AccountService {

   @Resource
   private TransactionSynchronizationRegistry reg;

   @Override
   public Account createAccount(Account account) {

    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
     ... 
   }
}

我收到了奇怪的行为:

  • 从装饰师登录

    tx (0): JavaEETransactionImpl: txId=6 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@68fb15d0]]] 
    
  • 第二个日志上的NullPointerException(reg为null)。

有人可以向我解释一下吗? Wheter AccountServiceBean类是否在与ApplicationFacade相同的事务中调用?

谢谢

2 个答案:

答案 0 :(得分:1)

首先:我不会将ejbs与cdi拦截器混在一起。 ejbs在拦截器实现上有它。

秒:拦截器在与拦截器所在的ejb相同的事务中执行。

可能的解决方案:

  • 创建正确的ejb拦截器
  • 将拦截器放在方法/类
  • 周围
  • 使用类似 logToDatabase(String message)的方法创建第二个ejb( MyLoggerBean ),并使用 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)<注释此方法< /强>
  • 拦截器里面的
  • 创建一个这样的类成员: @EJB private MyLoggerBean loggerBean
  • @AroundInvoke 带注释的方法中,您可以调用 loggerBean。 logToDatabase(...)

这将从拦截器所在的ejb当前事务内部创建一个新事务

- &GT;我知道我的英语不是很好。但我希望你明白我认为应该有效。如果我有时间,我在github上做例子......

答案 1 :(得分:0)

嗯......你在用什么容器?一般来说,我不会怀疑CDI装饰器在EJB上工作......我无法想到我遇到的JEE规范中的任何内容都可以提供证据。

虽然面对你的问题,我用拦截器做了这个,而不是装饰器。 EJB规范支持这些......无论如何,这是我的代码,你需要从你的情境中的上下文中获取变量:

import java.lang.reflect.Method;

import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

public class InvocationCountInterceptor {
    @Inject
    private InvocationCounter counter;

    @AroundInvoke
    public Object intercept(InvocationContext ctx) throws Exception {
        Object returnValue = ctx.proceed();
        Class<? extends Object> className = ctx.getTarget().getClass();
        Method methodName = ctx.getMethod();
        counter.incrementCounter(className, methodName);
        return returnValue;
    }
}

然后,无论您想要审核哪种EJB或EJB方法,我都添加了这个:@Interceptors(InvocationCountInterceptor.class)