【问题标题】:How to inject dynamic EntityManager into a Third Party Library如何将动态 EntityManager 注入第三方库
【发布时间】:2016-10-19 17:34:16
【问题描述】:

我有一个库,其中包含一些我想在其他项目中重用的功能。我的问题是我的服务需要写入数据库。我希望我的库使用注入我的服务的项目的数据源。

这是我的服务的最小设置

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

这是我的服务生产者的代码

@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 目标的替代方法?

【问题讨论】:

    标签: jpa jakarta-ee cdi


    【解决方案1】:

    经过多次试验,我无法通过上述代码动态生成 EntityManager。经过大量研究,我放弃了尝试从第三部分库之外传递名称。我会创建以下界面:

    public interface CustomEntityManager {
        EntityManager getEntityManager();
    }
    

    这意味着在使用 3rd 方服务的项目中,我可以创建以下实现来注入 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;
       }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-13
      • 1970-01-01
      • 2018-05-05
      • 1970-01-01
      相关资源
      最近更新 更多