Как использовать JPA EclipseLink для подключения к БД в многослойной среде (REST + EJB + Core)? - PullRequest
2 голосов
/ 19 августа 2010

Я использую сервер Glassfish v3.

Обычно соединение с БД с EJB3 + JPA (Eclipselink) осуществляется посредством внедрения, с помощью @PersistenceUnit или @ Persistencecontext.

Однако в моем приложении 3 слоя:

  • Ядро (содержит бизнес-логику, сущности, обработку исключений и т. Д.)

  • EJB поверх него, вызывающий правильные основные объекты и методы для выполнения работы. Этот EJB вызывается другими внутренними модулями нашей ERP.

  • слой REST поверх него для использования веб-сайтами внешнего интерфейса.

Я не хочу получать в EJB entityManager или EMF (фабрику EM), потому что я хочу, чтобы мой средний уровень не знал, что под ним используется БД. В конце концов, я мог бы решить поменять свою базовую реализацию на другую, не использующую БД.

Я вижу только два плохих решения:

  • 1) Добавляйте параметр EM каждый раз, когда я вызываю метод уровня ядра, который требует подключения к БД. Очень некрасиво и повторяет то, что я сказал выше.

  • 2) В каждом методе подключения ядра к базе данных я создаю фабрику, EM, использую их, а затем закрываю их оба.

Я пытался разрезать вещи посередине, имея одну фабрику на класс уровня Core, а 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:

  • Добавлен пустой файл beans.xml в каталог / META-INF на моем фляге CORE.

  • Родительский класс 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-запросе этих двух небольших аннотаций (с одной стороны, без состояния, с другой - с помощью Inject)?

Ответы [ 4 ]

1 голос
/ 19 августа 2010

С помощью JavaEE 6 вы можете определять классы базового уровня как Beans и вставлять туда ресурсыПожалуйста, проверьте контекст и внедрение зависимостей (CDI) с JavaEE 6.

0 голосов
/ 06 сентября 2010

Вот окончательный рабочий код, благодаря Блейзу:

  • Класс отца, который "получает" соединение

    import com.wiztivi.apps.wsp.billing.interfaces.bin.db.NewInterface; import javax.ejb.LocalBean; import javax.ejb.Stateless; import javax.naming.Context; import javax.naming.InitialContext; import 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-соединении с сущностями)

    import javax.ejb.LocalBean; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import 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 в любой класс «более высокого уровня», который вызовет EJB FatherService (в моем случае, классы REST). Уровень Core должен быть упакован как EJB, а также соединение, как в EAR

0 голосов
/ 24 августа 2010

Я не уверен, что это хороший ответ, но я нашел это: ссылка на форуме поговорка:

кажется, что взаимодействие между JPA и CDI были рассмотрены, но не сделал часть спецификации для некоторых неизвестных причина. Если я узнаю о причина, я буду обновлять эту тему. В в то же время, он был отправлен как обратная связь с соответствующими людьми. Так, это определенно не ошибка в GlassFish.

Итак, это объясняет, почему мой @Inject (класса, содержащего менеджер сущностей) в классе Java не работает?

0 голосов
/ 19 августа 2010

Что если у вас было два сессионных компонента? Один с внедренным EntityManager, который может использовать JTA, а другой - ваш текущий сессионный компонент.

В настоящее время я собираю серию для моего блога , используя сессионный компонент в качестве службы REST, используя EclipseLink & Glass Fish v3:

Ниже показано, как я внедряю EntityManager в мой сессионный компонент, который служит моей службой REST:

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:

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);
        }
    }

}
...