我有以下托管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);
。这是怎么造成的,我该如何解决?
答案 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
来注入您的服务,而不是@EJB
。here you can read further on the subject.