NullPointerException异常时试图访问JPA服务类

问题描述:

我有下面的托管beanNullPointerException异常时试图访问JPA服务类

@ManagedBean 
@RequestScoped 
public class customerBean { 

    private Customer customer; 
    private CustomerJpaController customerJpa; 

    @PostConstruct 
    public void init() { 
     customer = new Customer(); 
    } 

    public String addCustomer() throws Exception { 
     customerJpa.create(customer); 
     return "customer"; 
    } 

    // getter and setter 
} 

CustomerJpaController看上去象下面这样:

public class CustomerJpaController implements Serializable { 

    @PersistenceContext(unitName = "JFSCustomerPU") 
    private EntityManagerFactory emf = null; 
    private UserTransaction utx = null; 

    public CustomerJpaController(UserTransaction utx, EntityManagerFactory emf) { 
     this.utx = utx; 
     this.emf = emf; 
    } 

    // ... 
} 

addCustomer()从视图中调用,它抛出java.lang.NullPointerException在行customerJpa.create(customer);。这是如何造成的,我该如何解决这个问题?

+1

你必须@ @注入这个JPA或创建它的某个实例,因为变量'customerJpa'为null,你不需要初始化它。 – Geinmachi

这是我对事物的认识(它可能不是100%正确的,但它会给你一个总体思路):

凡在你的bean是您的服务实例?无处。换句话说,customerJpa为空。 启动与db的连接会加重资源。因此,不是你自己实例化不同的服务,而是打开关闭连接,容器拥有一个服务池,并为所有需要它的人提供免费服务(在你的情况下,你的bean需要一个服务)。您怎么要求容器给你一个服务:

注释@EJB服务上面:

@EJB 
private CustomerJpaController customerJpa; 

,我认为你缺少@Stateless以及

@Stateless 
public class CustomerJpaController... 

这是建议切换到@Named@RequestScoped(另一个包),而不是@ManagedBean。然后,您可以使用@Inject来注入您的服务,而不是@EJBhere you can read further on the subject.

在您的代码示例中,您的CustomerJpaController从未实例化过。所以,你会得到一个空指针异常。

我建议您切换到CDI并依靠其注入方法让实体管理器(工厂?)正确实例化,并在最后一次实例化时将其注入控制器。所以,要使用@Named而不是@ManagedBean。

所以,你会:

@Named 
@RequestScoped 
public class CustomerJpaController implements Serializable { 
    ... 
} 

(或任何范围更符合您的需要)

在我看来,你应该使用一个EntityManager(EM),而不是EntityManagerFactory的(EMF)在你的控制器中。

如果EMF是容器管理,您只有一个持久性单元,您可以使用标准的JPA @PersistenceContext注释:

@PersistenceContext 
private EntityManager entityManager; 

如果EMF没有被管理,您可以利用的deltaspike JPA module功率(记:deltaspike是对你有好处:-)),并在控制器中注入EntityManager:

@Named 
@RequestScoped 
public class CustomerJpaController implements Serializable { 
    @Inject 
    private EntityManager em; 
} 

这需要EntityManagerProducer类的实现,它可以有任何名称,但必须有一个方法注释@Produces @RequestScoped返回一个EntityManager,另一个采用用@Disposes注解的EntityManager参数。例如:

public class MyEntityManagerProducer { 

    @Inject 
    @PersistenceUnitName("myPU") 
    private EntityManagerFactory emf; 

    @Produces 
    @RequestScoped 
    public EntityManager createEntityManager() { 
     return emf.createEntityManager(); 
    } 

    public void disposeEntityManager(@Disposes em) { 
     if (em.isOpen()) { 
      em.close(); 
     } 
    } 

注@PersistenceUnitName( “myPU”)的使用,该deltaspike注释将处理EMF的instanciation。

如果您有多个持久性单元,因为在现实世界中经常出现这种情况,您可以使用限定符将它们分开。要声明一个限定词,声明@interface与以下注释:

@Target({ FIELD, METHOD, PARAMETER, TYPE }) 
@Retention(RUNTIME) 
@Documented 
@Qualifier 
public @interface MyQualifier { 
} 

然后,这个限定词添加到所有@Produces,@Disposes和@Inject,允许CDI来决定哪个持久化单元/实体管理你愿意使用方法:

public class MyEntityManagerProducer { 

    @Inject 
    @PersistenceUnitName("myPU") 
    private EntityManagerFactory emf; 

    @Produces 
    @MyQualifier 
    @RequestScoped 
    public EntityManager createEntityManager() { 
     return emf.createEntityManager(); 
    } 

    public void disposeEntityManager(@Disposes @MyQualifier em) { 
     if (em.isOpen()) { 
      em.close(); 
     } 
    } 

和控制器:

@Named 
@RequestScoped 
public class CustomerJpaController implements Serializable { 
    @Inject 
    @MyQualifier 
    private EntityManager em; 
} 

所有这些都需要CDI。配置CDI不仅仅是对你的问题的简短回答。我所有的项目中都有我的use OpenWebBeansWeld也很受欢迎。