尝试访问JPA服务类时出现NullPointerException

时间:2015-09-26 09:50:23

标签: jsf jpa nullpointerexception

我有以下托管bean

@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);。这是怎么造成的,我该如何解决?

2 个答案:

答案 0 :(得分:1)

在您的代码示例中,您的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"),即处理EMF实例化的deltaspike注释。

如果你有多个持久性单元,就像在现实世界中一样,你可以用限定符将它们分开。要声明限定符,请使用以下注释声明@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 OpenWebBeans在我的所有项目中。 Weld也很受欢迎。

答案 1 :(得分:0)

这是我对事物的理解(它可能不是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.