2

我正在使用 Glassfish v3 服务器。

通常与 EJB3 + JPA (Eclipselink) 的数据库连接是通过注入完成的,使用 @PersistenceUnit 或 @Persistencecontext。

但是,我的 App 中有 3 层:

  • 核心(包含业务逻辑、实体、异常处理等)

  • 在它之上的 EJB,调用正确的核心对象和方法来完成这项工作。这个 EJB 被我们 ERP 的其他内部模块调用。

  • 其之上的 REST 层供前端网站使用。

我不想在 EJB 中获得 entityManager,也不想获得 EMF(EM 工厂),因为我希望我的中间层不知道它下面使用了一个 DB。毕竟,我以后可以决定将我的核心实现更改为不使用 DB 的实现。

我只看到两个不好的解决方案:

  • 1)每次调用需要DB连接的核心层方法时添加一个EM参数。非常丑陋,与我上面所说的背道而驰。

  • 2)在需要数据库连接的每个核心方法中,我创建了一个工厂、一个 EM,使用它们,然后将它们都关闭。

我试图在中间切开一些东西,每个核心级别的类都有一个工厂,并且在每种方法中都创建和关闭了 EM。但我仍然有这样的内存泄漏:

javax.servlet.ServletException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.0.v20091127-r5931): org.eclipse.persistence.exceptions.DatabaseException

Internal Exception: java.sql.SQLException: Error in allocating a connection. Cause: In-use connections equal max-pool-size and expired max-wait-time. Cannot allocate more connections.

我想这是因为如果我的 EJB 方法之一使用 10 个不同的对象,它会创建 10 个 EM 工厂,并且没有一个是关闭的。

Core 对象中的典型用法示例:

 EntityManager em = emf.createEntityManager();
 em.getTransaction().begin();
 // do some stuff with em; for example persist, etc
 em.flush();
 em.close();

我应该去解决方案2吗?有没有办法在这个核心级别使用单个 EM 工厂?我似乎 JPA 规范假设您将仅在 EJB 级别使用实体,这在多层应用程序中是不好的。

编辑:这是尝试 @Inject 后的当前状态:

  • 在我的 CORE jar 的 /META-INF 目录中添加了一个空 beans.xml 文件。

  • 父 DAO 类现在是这样的:

    公共类 ExampleBZL {

     public EntityManagerFactory emf;
     @Inject public Emf emfobject;
    
     public ExampleBZL()
     {
        this.emf = emfobject.emf;
     }
    
  • Emf 类非常简单,而且是无状态的。

    @Stateless 公共类 Emf 实现 EmfAbstract {

    @PersistenceUnit(unitName = Setup.persistenceUnitName)
    public EntityManagerFactory emf;
    
    public Emf()
    {
    }
    

    }

我一定是做错了什么,但注入不起作用,尽管在 glassfish 中我在引擎列表中看到“[ejb,weld,web]”,因此加载了 CDI。

Servlet.service() for servlet Jersey Web Application threw exception
java.lang.NullPointerException
    at com.blablabla.core.bizlogic.ExampleBZL.<init>(ExampleBZL.java:40)

我是否缺少其他注释?用这两个小注释(一侧是无状态,另一侧是注入)在 JAR 中进行注入真的有效吗?

4

4 回答 4

1

使用 JavaEE 6,您可以将核心级别的类定义为 Bean 并在其中注入资源。请检查 JavaEE 6 的上下文和依赖注入 (CDI)。

于 2010-08-19T12:32:03.723 回答
0

如果你有两个会话 bean 怎么办?一个注入了可以利用 JTA 的 EntityManager,另一个是您当前的会话 bean。

我目前正在 使用 EclipseLink 和 Glass Fish v3 使用会话 bean 作为 REST 服务在我的博客上整理一个系列:

下面是我如何在充当 REST 服务的会话 bean 上注入 EntityManager:

package org.example.customer;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.ws.rs.Path;

import org.eclipse.persistence.rest.JPASingleKeyResource;

@Stateless
@LocalBean
@Path("/customers")
public class CustomerService  {

    @PersistenceContext(unitName="CustomerService", type=PersistenceContextType.TRANSACTION)
    EntityManager entityManager;

}

您可以使用 @EJB 注释链接会话 bean:

package org.example;

import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.naming.Context;
import javax.naming.InitialContext;

@Stateless
@LocalBean
@EJB(name = "someName", beanInterface = CustomerService.class)
public class OtherSessionBean {

    public Customer read(long id) {
        try {
            Context ctx = new InitialContext();
            CustomerService customerService = (CustomerService) ctx.lookup("java:comp/env/someName");
            return customerService.read(id);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

}
于 2010-08-19T17:52:12.563 回答
0

我不确定这是一个好的答案,但我发现了这个: 论坛链接说:

似乎考虑了 JPA 和 CDI 之间的交互,但由于某些未知原因未将其纳入规范。如果我知道原因,我会更新这个帖子。同时,它已作为反馈发送给适当的人。因此,这绝对不是 GlassFish 中的错误。

那么,这是否解释了为什么我在 java 类中的@Inject(包含实体管理器的类)不起作用?

于 2010-08-24T08:49:00.573 回答
0

这是最终的工作代码,感谢 Blaise:

  • “接收”连接的父类

    导入 com.wiztivi.apps.wsp.billing.interfaces.bin.db.NewInterface;导入 javax.ejb.LocalBean;导入 javax.ejb.Stateless;导入 javax.naming.Context;导入 javax.naming.InitialContext;导入 javax.persistence.EntityManager;

    @Stateless
    @LocalBean
    public class FatherService {
    
     public EntityManager em;
    
     public FatherService()
     {
     }
    
    
    
     public EntityManager getGoodEm()
     {
        try {
            Context ctx = new InitialContext();
            NewInterface dp = (NewInterface) ctx.lookup("java:global/billing-ear/billing-connection/DataProvider");
            em = dp.getEm();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
         return em;
     }
    
    }
    
  • “提供”连接的类(在单独的连接 JAR 中,带有实体)

    导入 javax.ejb.LocalBean;导入 javax.ejb.Stateless;导入 javax.persistence.EntityManager;导入 javax.persistence.PersistenceContext;导入 javax.persistence.PersistenceContextType;

    @Stateless @LocalBean 公共类 DataProvider 实现 NewInterface {

    @PersistenceContext(unitName=Setup.persistenceUnitName, type=PersistenceContextType.TRANSACTION)
    public EntityManager entityManager;
    
    public DataProvider() {
    }
    
    @Override
    public EntityManager getEm()
    {
        return entityManager;
    }
    

    }

重要的事情:您必须将@Stateless 放在将调用FatherService EJB(在我的情况下为REST 类)的“更高级别”层的任何类上。核心层必须打包为EJB,连接也是,都在耳朵里

于 2010-09-06T16:28:37.060 回答