我有一个库,其中包含一些我希望在其他项目中重用的功能。我的问题是我的服务需要写入数据库。我希望我的库能够使用注入我服务的项目的数据源。
以下是我服务的最小设置
@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 Producer
@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());
}
}
以下是我的Service Producer的代码
@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)
...
看起来所有不同级别的提供程序都阻止了对CustomService.createObject()方法调用的@Transactional传播事务。有没有人深入了解为什么这是实现我注入动态EntityManager的目标的另一种方式?
答案 0 :(得分:0)
经过多次试验,我无法通过上面的代码动态生成EntityManager。经过大量的研究,我放弃了尝试从第三部分库外传递名称。我想创建以下界面:
public interface CustomEntityManager {
EntityManager getEntityManager();
}
这意味着在使用第三方服务的项目中我可以创建以下实现来注入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;
}
}