【问题标题】:EntityManager null pointer exception from Persistence Context Java EE来自持久性上下文 Java EE 的 EntityManager 空指针异常
【发布时间】:2017-03-19 12:21:24
【问题描述】:

我在使用由 EJB Container 管理的 entityManager 时遇到问题。

在我的耳朵里,我有 3 个模块:

-domain - 实体类和 DAO 实现将接口作为 EJB。

-services - 我将有与处理请求相关的逻辑,根据库异常(例如数据库异常)调用业务异常。

-Rest 服务 - 在那里我将有其他端点来接收和发送一些业务对象。

所以主要问题是在服务层使用 EJB,我可以将 Rest 端点类、服务类和 DAO 类声明为 EJB。但是这样我就无法捕获与数据库相关的异常,因为当我变红时,我在 WildFly 提供的 EJB 容器中得到 transactionRollback 异常,并且这个异常在提交事务时存在。

我考虑过从服务类中删除@Stateless 注释,但是当我这样做时,EJB 容器不要在我的 DAO 类中注入 entityManager。我也尝试在服务类中使用@Inject 注解,但在 Dao 类中的 entityManager 上仍然得到相同的 NullPointer 异常。

下面我在我的项目中添加示例代码实现:

DAO 类:

@Stateless
public class CardDaoImpl implements CardDao {

@PersistenceContext(unitName = "test")
protected EntityManager em;

public CardBase addCardToDatabase(CardBase card) {
    em.persist(card);
    return card;
}

Service Class(这里我要处理businessExceptions):

@Stateless
public class TestService implements TestServiceIf {

@EJB(beanInterface = CardDao.class)
private CardDao dao;

@Override
public CardBase addCard(CardBase card) {
    dao.addCardToDatabase(card);
    return card;
}

}

休息端点类:

@Stateless
@Path("/test")
public class Test {

@EJB(beanInterface = TestServiceIf.class)
private TestServiceIf service;

@GET
@Path("/test")
@Produces(MediaType.APPLICATION_JSON)
public String addCard() {
    CardBase card = new SampleCard();
    //Setting card object fields//
    service.addCard(card);
    return "Hello";
}

}

这只是我发现的一种解决方案。但是抛出我的 businnesExceptions 是怎么回事?也许将它们扔到这个服务层不是一个好习惯?

【问题讨论】:

  • 你的服务器和你的ejb版本是什么?不幸的是,根据服务器的说法,即使对于 EJB 规范,您也有特殊性。
  • 我正在使用 Wildfly 10。在此应用程序服务器的模块中,我找到了 EJB3.2 api

标签: jpa dependency-injection nullpointerexception ejb entitymanager


【解决方案1】:

使用 EJB 3.X,您对 ejb 方法中发生的异常有一些特殊性。

-services - 我会有与处理请求相关的逻辑,基于库异常调用业务异常(例如 示例数据库异常)。

默认情况下,容器会在一个技术异常 (EJBTransactionRolledbackException) 中包装它认为是意外的 EJB 中发生的所有异常。以同样的方式,您可以捕获应用程序中的所有意外异常。除了异常包装之外,容器还会触发当前事务的回滚。
为避免容器封装技术异常,您的业务异常不得被容器视为意外。

一般来说,你有两种情况:

  • 要么,检查这些。在这种情况下无事可做。

  • 要么,这些都是运行时。您必须在它们上添加ApplicationException 注释或指定自 EJB 的 xml 配置以来的应用程序异常行为。

来自ApplicationExceptionjavadoc:

:应用于异常以表示它是 应用程序异常,应直接报告给客户端 (即展开)。

最后,回滚操作可能需要也可能不需要。你可以这样配置:

@ApplicationException(rollback=true)
public class MyBusinessRuntimeException extends RuntimeException {

@ApplicationException(rollback=false)
public class MyBusinessRuntimeException extends RuntimeException {

使用 Weblogic,我记得我还必须在方法的签名中声明这些异常。


更新评论:

但首先我需要从第三方库中捕获异常,例如 约束值异常。

你有两种方法。在可能出现问题的地方显式处理异常或横向处理异常。 我想你指的是ConstraintViolationException

  • 当异常是特定的并且需要处理上下文时处理发生的异常:

如果您希望在特定场景中出现特定的第三方异常,您想捕获它以返回您的业务异常,您应该在第三方异常出现时立即捕获它。
首先,一旦我们知道如何处理异常,就立即捕获它是一个好习惯。
其次,避免让异常上升到下一层,因此变成EJBTransactionRolledbackException异常。
但是,有时,出于充分的理由,可能会以横向方式处理异常类型。
在这种情况下,您可以对该异常使用横向处理。

  • 当异常包含需要处理的信息时的横向处理:

如果您想以横向方式处理预期的运行时异常,您可以使用 JEE 拦截器并捕获它来抛出您自己的业务异常。
在这种情况下,您必须在拦截器中定义捕获异常的原因与您的自定义异常之间的映射。

执行您需要的拦截器示例(它不处理所有场景,只是一个名义场景):

public class WrappingInBusinessExceptionInterceptor {

    @AroundInvoke
    public Object handleExceptions(final InvocationContext ctx) throws Exception {

        try {
            Object result = ctx.proceed();
            return result;
        }

        catch (Exception exception) {
            // log the root exception
               ...
               Exception causeException = e.getCause();
               if (causeException !=null &&
                   causeException.getClass()==ConstraintValueException.class){
                  // throw your custom exception
                  String businessMsg =  
                         retrieveBusinessMessageFrom(causeException);
                  throw new MyBusinessException(businessMsg);
                }
        }                               

    }

【讨论】:

  • 好的,这是解决方案的第二部分。但首先我需要从第三方库捕获异常,例如 ConstraintValueException 或类似的东西,当我向我的数据库中添加具有现有键值的东西时。当我发现我会抛出我的 businnesException 以在前端处理它并提供正确的消息。
  • 好的,但你不明白我的意思。我的代码仅在我使用注释 @Stateless 标记所有类使它们成为 EJB 时才有效。所以容器在收到休息请求后可能会打开一个事务并在数据超出 EJB 类后关闭它。例如,我已经在 servlet 中对其进行了测试。在 servlet 类中一切正常,我可以在不是 EJB 的 Servlet 类中注入我的服务 EJB 对象,这样我就可以捕获我的 RolledBackTransactionException。
  • 在添加的代码中,我使用 REST 作为端点,我必须用 @Stateless 注释标记它。如果我没有标记这个类,我会在 entityManager.persist() 上得到 nullPointerException,因为容器不会注入它。
  • 我现在明白你的意思了。不幸的是,我认为这是容器中 Java EE 组件之间关系的副作用,因为您说它可以与 Servlet 一起使用。但是,为什么将您的休息服务声明为无状态 EJB 会打扰您呢?它有优势,特别是如果您在较低层使用 EJB。这里是另一篇文章(您可能会看到),作者有类似的问题:stackoverflow.com/questions/22022019/...。关于在将 jax-rs 与 ejb 一起使用时是否强制无状态的 Oracle Java EE 文档不是很清楚:docs.oracle.com/javaee/6/tutorial/doc/gkncy.html
  • 我找到了解决方案。我已经用 CDI 注入了它(在类上使用 managedBean 注释,在对象上使用 @Inject)。我还必须使用特定的 xml 标头将 beans.xml 文件添加到我的项目中(添加到 EJB 中的 META-INF 和 WEB 中的 WEB-INF)。
猜你喜欢
  • 2013-06-08
  • 1970-01-01
  • 2015-03-23
  • 1970-01-01
  • 2014-12-02
  • 1970-01-01
  • 2015-11-14
  • 1970-01-01
  • 2016-11-07
相关资源
最近更新 更多