У меня есть библиотека с некоторой функциональностью, которую я хочу повторно использовать в других проектах. Моя проблема заключается в том, что моя служба требует записи в базу данных. Я хотел бы, чтобы моя библиотека использовала источник данных проекта, который вводит мой сервис.Как добавить динамический 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?