2010-08-19 1 views
2

Я использую сервер Glassfish v3.Как использовать JPA EclipseLink для подключения к БД в многоуровневой среде (REST + EJB + Core)?

Обычно соединение DB с EJB3 + JPA (Eclipselink) выполняется посредством инъекции, с @PersistenceUnit или @Persistencecontext.

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

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

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

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

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

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

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

  • 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 заводов, и ни один из них не будет закрыт.

Пример типичного использования в ядре объекта:

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

Должен ли я пойти на решение 2? Есть ли способ использовать одну фабрику EM на этом базовом уровне? Мне кажется, что спецификация JPA предполагает, что вы собираетесь использовать объекты только на уровне EJB, что плохо в многослойных приложениях.

EDIT: здесь текущее состояние после попытки @Inject:

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

  • Родительский класс DAO теперь так:

    общественного класса ExampleBZL {

    public EntityManagerFactory emf; 
    @Inject public Emf emfobject; 
    
    public ExampleBZL() 
    { 
        this.emf = emfobject.emf; 
    } 
    
  • Класс Emf очень прост, и Stateless.

    @Stateless общественного класса Emf реализует EmfAbstract {

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

    }

я должен делать что-то неправильно, но инъекции не работает, Altough в 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 на другом)?

ответ

0

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

В настоящее время я собираю серию на my blog используя сессионный компонент в качестве службы 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); 
     } 
    } 

} 
+0

Спасибо, я дождаться полной серии. Не знаете, как вы назовете «EntityManagered» bean из сеанса один, будет ли это снова с инъекцией? – spiritoo

+0

Я опубликовал больше примера REST в своем блоге (просто отсутствует часть клиента). вернемся к вам в аспекте взаимодействия сеансового компонента. –

+0

Разница с вашим примером: у вас есть слой REST + EJb в том же классе. Для нас это не так, потому что мы хотим иметь возможность добавлять другие интерфейсы (скажем, SOAP или что-то еще) на вершине EJB. Кроме того, основная проблема заключается в том, чтобы получить менеджера организации OUTSIDE EJB в базовом классе .... Итак, я попытался создать еще один EJB, который содержит фабрику entityManager. Вместо того, чтобы делать @Inject, я делаю @EJB в классе «обработка данных» моего отца ... и также получаю нулевой указатель. – spiritoo

1

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

+0

Очень интересно. Никогда не использовал CDI до сих пор, поэтому я попытался добавить @Inject в суперкласс класса Core DAO, см. Выше, но не могу заставить его работать :-( – spiritoo

+0

CDI сейчас очень прослушивается, в зависимости от того, как вы пакет вещи, это работает или нет (см. например https://glassfish.dev.java.net/issues/show_bug.cgi?id=11653 и т. д.). Поэтому я отказался от этой идеи. Спасибо в любом случае. – spiritoo

0

Я не являюсь уверен, что это хороший ответ, но я нашел это: link of forum говоря:

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

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

+0

Нет, это не так. Вышеизложенное означает, что вы не можете вводить объекты JPA. –

0

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

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

    импорта com.wiztivi.apps.wsp.billing.interfaces.bin. db.NewInterface; import javax.ejb.LocalBean; импорт javax.ejb.Stateless; import javax.naming.Context; import 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 любого класса слоя "более высокого уровня" », который будет вызывать EJB от FatherService (в моем случае, классы REST). Уровень ядра должен быть упакован как EJB, а соединение тоже, как в EAR

Смежные вопросы