【问题标题】:CDI inject a @PersistenceContext based on user inputCDI 根据用户输入注入 @PersistenceContext
【发布时间】:2014-09-30 02:45:32
【问题描述】:

我正在尝试弄清楚如何使用 CDI。我正在开发一个用户能够连接到多个数据库的应用程序。应用程序询问他们想要连接到哪个,他们也可以断开连接并连接到不同的数据库。这是一个使用 Seam for CDI 和 Hibernate for JPA 的 Java SE 应用程序。我想弄清楚的是如何利用 CDI 连接@PersistenceContext,同时使其动态化,以便可以访问不同的数据库。我只是不确定我将使用 CDI 执行此操作的模式或技术。想法?

【问题讨论】:

    标签: jpa cdi inject


    【解决方案1】:

    所以我想我通过使用javax.enterprise.inject.Instance 想出了我想要做什么。首先定义一个简单的bean:

    @Alternative
    public class Foo {
        private int value;
    
        public void setValue(int value) {
            this.value = value;
        }
        public int getValue() {
            return value;
        }
    }
    

    它被定义为@Alternative,因此CDI 不会混淆它和生产者方法(如下所示)。更好的解决方案是将Foo 定义为接口,然后FooImpl 将使用@Typed 进行注释,因此CDI 认为它只是FooImpl bean 类型。不管怎样,接下来是生产者类。

    @ApplicationScoped
    public class FooProducer {
        private int value;
        public FooProducer() {
            value = -1;
        }
        public int getValue() {
            return value;
        }
        public void setValue(int value) {
            this.value = value;
        }
        @Produces
        public Foo getFoo() {
            Foo p = new Foo();
            p.setValue(getValue());
            return p;
        }
    }
    

    getFoo() 方法生成具有不同值的新 Foo 对象。该值可以通过setValue(int) 方法更改。接下来我使用javax.enterprise.inject.Instance 注入Foo

    @Inject
    Instance<Foo> fooInstance;
    
    @Inject
    FooProducer fooProducer;
    
    ....
    
    fooProducer.setValue(10);
    
    Foo foo = fooInstance.get();
    System.out.printf("foo = %s, %d\n", foo.getValue());
    
    fooProducer.setValue(10000);
    
    foo = fooInstance.get();
    System.out.printf("foo = %s, %d\n", foo.getValue());
    

    这里我使用注入的fooProducer.setValue() 方法来改变生产者将如何产生Foo。当fooInstance.get() 第一次被调用时,Foo 将包含值 10,第二次将包含值 10000。

    鉴于这个简单的示例,很容易应用在运行时将EntityManager 实例获取到不同的数据库。生成 EntityManager 的代码还有更多内容,但不会太多。

    【讨论】:

      【解决方案2】:

      PersistenceContext 由 EJB 容器处理。由于您不在 EJB 容器中,因此请忘记持久性上下文并考虑使用 CDI 生产者。

      @ApplicationScoped
      public class EntityManagerFactoryProvider {
      
        private EntityManagerFactory emf;
      
        public void voidApplicationEMF() {
          //load the EMF here based on dynamic properties
          this.emf = Persistence.createEntityManagerFactpry(Map of loaded properties);
        }
      
        @Produces
        public EntityManager em() {
          return emf.createEntityManager();
        }
      }
      

      然后在你的代码中的某个地方

      @SessionScoped
      public class MyEntityController {
        @Inject
        private EntityManager emf;
      }
      

      还有交易等问题需要处理

      在 JSE 环境中,您必须显式启动和提交/回滚事务。 由于您使用的是 CDI,因此您可以使用拦截器来启动/检查事务状态并根据需要回滚/提交。

      @Interceptor
      public class MyInterceptor {
        @Inject
        private EntityManager em;
      
        @AroundInvoke
        public Object intercept(InvocationContext ic) {
          try {
            //initiate transaction
            em.getTransaction().start ....
            return ic.proceed();
          }catch(Exception ex) { //rollback if necessary }
          finally {//commit transaction if necessary }
        }
      }
      

      【讨论】:

      • >PersistenceContext is handled by an EJB container - 这是不正确的。 JPA 是独立于 EJB 的规范,持久性上下文通常由 JPA 实现的 Java EE 容器特定部分设置。
      • @ArjanTijms 实际上 JPA​​ 规范是 EJB 规范的一部分,只有规范文档是分开的
      • 我不清楚如何实现您评论为“//根据动态属性在此处加载 EMF”的部分我不希望生产者类负责用户界面。我正在尝试找出 MVC 方式 - 视图处理获取动态信息,然后以某种方式将其传递给生产者。现在我在打字时正在考虑这个问题,也许 CDI 事件是要走的路?生产者类观察事件并在生产者类中设置动态数据时使用的生产EntityManager?数据库更改时如何更新注入点?
      • 如果 db 属性来自最终用户界面,这可能是一个有效的选项(我希望它不是公共界面:sql injection....)
      • >"实际上 JPA​​ 规范是 EJB 规范的一部分,只有规范文档是分开的" - 你为什么这么认为? JPA 只是 Java EE 5/EJB 3.0 中 EJB 规范的短暂部分。它通过 JSR 317 在 Java EE 6/EJB 3.1 中成为了自己的规范。请参阅 jcp.org/en/jsr/detail?id=317
      猜你喜欢
      • 2015-02-14
      • 2016-01-19
      • 2019-06-15
      • 2011-06-10
      • 2017-05-19
      • 1970-01-01
      • 2018-07-07
      • 2017-03-21
      • 2016-01-30
      相关资源
      最近更新 更多