2016-10-19 5 views
0

У меня есть библиотека с некоторой функциональностью, которую я хочу повторно использовать в других проектах. Моя проблема заключается в том, что моя служба требует записи в базу данных. Я хотел бы, чтобы моя библиотека использовала источник данных проекта, который вводит мой сервис.Как добавить динамический EntityManager в стороннюю библиотеку

Вот минимальная настройка моей службы

@Stateless 
public class CustomService { 
    //to be added in producer 
    private EntityManager em; 
    private Principal principal; 

    //default constructor 
    public CustomService() {} 
    //custom constructor called in provider 
    public CustomService(Principal p, EntityManager e) { 
     principal = p; 
     em = e; 
    } 

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    @Transactional 
    public CustomJPAObject createObject(...params...) { 
     //create JPA Object 
     em.persist(customObject); 
     em.flush(); 
     return customObject; 
    } 

} 

Я создал аннотаций для подмены источника данных

@Qualifier 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.TYPE,ElementType.FIELD, ElementType.METHOD}) 
public @interface DynamicDS { 
    @Nonbinding String value() default ""; 

} 

Я также создал Singleton быть EntityManager Производитель

@Singleton 
public class CustomEMProducer { 
    private Map<String, EntityManagerFactory> emfMap = new HashMap<>(); 

    @Produces @Dependent @DynamicDS 
    public EntityManager produceEntityManager(InjectionPoint injectionPoint) { 
     String dataSourceName = null; 
     for(Annotation qualifier: injectionPoint.getQualifiers()) { 
      if(qualifier instanceof DynamicDS) { 
       DynamicDS dds = (DynamicDS) qualifier; 
       dataSourceName = dds.value(); 
       break; 
      } 
     } 
     EntityManagerFactory emf = emfMap.get(dataSourceName); 
     if (emf == null) { 
      emf = Persistence.createEntityManagerFactory(dataSourceName); 
      emfMap.put(dataSourceName, emf); 
     } 
     return emf.createEntityManager(); 
    } 

    @PostConstruct 
    public void cleanup() { 
     emfMap.entrySet().stream().forEach(entry -> entry.getValue().close()); 
    } 
} 

Вот код для моего Сервис-продюсера

@Stateless 
public class CustomServiceProvider { 
    @Inject private Principal principal; 

    @Produces @Dependent @DynamicDS 
    public BackgroundJobService getBackgroundJobService(InjectionPoint injectionPoint) throws EntityManagerNotCreatedException { 
     Annotation dsAnnotation = null; 
     for(Annotation qualifier: injectionPoint.getQualifiers()) { 
      if(qualifier instanceof BackgroundJobDS) { 
       dsAnnotation = qualifier; 
       break; 
      } 
     } 
     if (dsAnnotation != null) { 
      EntityManager em = CDI.current().select(EntityManager.class, dsAnnotation).get(); 
      CustomService service = new CustomService(principal, em); 
      return service; 
     } 
     throw new EntityManagerNotCreatedException("Could not Produce CustomService"); 
    } 
} 

Ниже, где я пытаюсь привнести мою новую услугу

@Stateless 
public class ProjectService { 
    @Inject @DynamicDS("project-ds") CustomerService service; 

    public CustomObject create(...params...) { 
     return service.createObject(...params...); 
    } 
} 

Когда я раскрываю свой код и попытаться вызвать впрыскивается службу я получаю следующее сообщение об ошибке:

Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress 
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1171) 
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1332) 
    ... 

It похоже, что все разные уровни провайдеров не позволяют @Transactional в вызове метода CustomService.createObject() распространять транзакцию. Кто-нибудь знает, почему это так, или альтернативный способ выполнить мою задачу инъекции динамического EntityManager?

ответ

0

После долгих экспериментов я не смог получить динамическое создание EntityManager через указанный выше код. После долгих исследований я отказался от попыток передать имя из-за пределов библиотеки третьей части. Я бы до создания следующего интерфейса:

public interface CustomEntityManager { 
    EntityManager getEntityManager(); 
} 

Это означало, что внутри проекта, который использует службу 3-й партии я могу сделать создать следующую реализацию впрыснуть EntityManager

public ProjectSpecificEntityManager implements CustomEntityManager { 
    @PersistenceContext(unitname = "project-ds") 
    private EntityManager em; 

    public EntityManager getEntityManager() { 
     return em; 
    } 
} 

мне пришлось обновить мой CustomService к следующим

@Stateless 
public class CustomService { 
    //Ignore warning about no bean eligible because it is intended 
    //that the project that uses this library will provide the 
    //implementation 
    @SuppressWarnings("cdi-ambiguous-dependency") 
    @Inject 
    CustomEntityManager cem; 

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    @Transactional 
    public CustomJPAObject createObject(...params...) { 
     //create JPA Object 
     cem.getEntityManager().persist(customObject); 
     return customObject; 
    } 
} 
Смежные вопросы