【问题标题】:How to inject persistence context to different data source programmatically如何以编程方式将持久性上下文注入不同的数据源
【发布时间】:2011-07-03 12:22:31
【问题描述】:

在标准 EJB 3 中,当注入实体管理器时,持久性单元(指数据源)被硬编码到注解中:(或 xml 文件)

@PersistenceContext(unitName = "myunit")
private EntityManager entityManager;

有没有办法使用实体管理器,但在运行时按名称选择数据源?

【问题讨论】:

    标签: jpa persistence ejb-3.0 entitymanager


    【解决方案1】:

    使用 EclipseLink,您可以设置在您的应用服务器中配置的 DataSource。

    import org.eclipse.persistence.config.PersistenceUnitProperties;
    ...
    
    
    ....
    Map props = new HashMap();  
    props.put(PersistenceUnitProperties.JTA_DATASOURCE, "dataSource");  
    EntityManagerFactory  emf = Persistence.createEntityManagerFactory("UNIT_NAME", props);
    EntityManager em = emf.createEntityManager();
    

    PU_NAME指的是文件persistence.xml中使用的名称
    dataSource 将应用服务器中用于 jdbc 资源的名称称为“jdbc/sample”

    【讨论】:

    • 请注意,这将创建一个应用程序管理的 EntityManager。原始问题引用了使用容器管理的 EntityManager 的代码。
    【解决方案2】:
    • 在 persistence.xml 中配置所需的数据源和持久单元。
    <persistence-unit name="UNIT_NAME" transaction-type="JTA">
          <provider>PERSISTENCE_PROVIDER</provider>
              <jta-data-source>java:DATA_SOURCE_NAME</jta-data-source>
    </persistence-unit>
    
     -- other units  
    

    现在,您可以在运行时为所需的持久性单元构建实体管理器。为每个数据源创建单独的持久性单元。

    //---
    EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName);
    EntityManager em = emf.createEntityManager();
    //---
    
    • 此外,您还可以通过提供 db-url、userName 等属性映射来构建工厂。
    createEntityManagerFactory(persistenceUnitName,propertiesMap);
    

    这将使用给定的属性为命名的持久性单元创建并返回一个 EntityManagerFactory。因此,您可以在运行时相应地更改属性。

    【讨论】:

    • 请注意,这也会创建一个应用程序管理的 EntityManager。原始问题引用了使用容器管理的 EntityManager 的代码。 @Monnie 的答案也适用于应用程序管理的 EntityManager。
    • @MozartBrocchini OP 显示了由容器管理的当前代码,但没有提到这是必要的。需要的是能够以编程方式选择数据源。
    【解决方案3】:

    有可能!我已经完成了,它可以在 JBoss AS 和 WebSphere 下运行。

    我使用了一个自定义的持久化提供程序,它扩展了org.hibernate.ejb.HibernatePersistence(你需要modify a private static final field 将你的持久化提供程序名称设置为org.hibernate.ejb3.Ejb3Configuration.IMPLEMENTATION_NAME:这是一种黑魔法,但它有效)。确保您的persistence.xml 的持久性单元在&lt;provider&gt; 标记中设置了自定义提供程序,并且您的自定义提供程序已在META-INF/services/javax.persistence.spi.PersistenceProvider 中注册。

    我的提供程序覆盖了称为 Java EE 容器的 createContainerEntityManagerFactory(PersistenceUnitInfo,Map) 方法(对于 JTA 数据源,但对于非 JTA 数据源也很容易):

    @Override
    public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {
    
        // load the DataSource
        String newDataSourceName = ...; // any name you want
        DataSource ds = (DataSource)(new InitialContext().lookup(newDataSourceName));
    
        // modify the datasource
        try {
            try {
                // JBoss implementation (any maybe other Java EE vendors except IBM WebSphere)
                Method m = info.getClass().getDeclaredMethod("setJtaDataSource", DataSource.class);
                m.setAccessible(true);
                m.invoke(info, ds);
    
            } catch (NoSuchMethodException e) {
                // method does not exist (WebSphere?) => try the WebSphere way
    
                // set the datasource name
                Method m = info.getClass().getDeclaredMethod("setJtaDataSource", String.class);
                m.setAccessible(true);
                m.invoke(info, newDataSourceName);
    
                // do the lookup
                Method m2 = info.getClass().getDeclaredMethod("lookupJtaDataSource", String.class);
                m2.setAccessible(true);
                m2.invoke(info);
            }
        } catch (Throwable e) {
            throw new RuntimeException("could not change DataSource for "+info.getClass().getName());
        }
    
        // delegate the EMF creation
        return new HibernatePersistence().createContainerEntityManaferFactory(info, map);
    }
    

    createEntityManagerFactory(String,Map) 也被覆盖但更简单:

    @Override
    public EntityManagerFactory createEntityManagerFactory(String persistenceUnitInfo, Map map) {
    
        // change the datasource name
        String newDataSourceName = ...; // any name you want
        if (map==null) map = new HashMap();  
        map.put(HibernatePersistence.JTA_DATASOURCE, newDataSourceName);  
    
        // delegate the EMF creation
        return new HibernatePersistence().createEntityManaferFactory(persistenceUnitInfo, map);
    }
    

    注意我这里只写了核心代码。事实上,我的持久化提供程序还有很多其他功能:

    • 检查 DataSource 是否已启动并正在运行
    • 为 JBoss 或 WebSphere 设置事务管理器
    • 缓存 EMF 以降低内存使用率
    • 重新配置 Hibernate 查询计划缓存以减少内存使用量
    • 注册 JMX bean(以允许多个 EAR 获得相同的持久性单元名称)

    【讨论】:

      【解决方案4】:

      我想指出的用法

      Persistence.createEntityManagerFactory(persistenceUnitName)
      

      在 Nayan 的回答中推荐按 JPA 规范 (JSR 317) 分类如下(“9.2 Java SE 环境中的引导”中的脚注):

      “Java EE 容器可能支持使用这些 Java SE 引导 API;但是,不需要支持此类使用。”

      所以这不是 EJB 的标准解决方案。无论如何,我可以确认这在 EclipseLink 中有效。

      注意:我还不能发表评论。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-27
        • 2014-01-18
        • 2019-03-18
        • 2010-12-14
        • 1970-01-01
        • 2013-03-06
        • 2020-11-29
        • 2014-02-20
        相关资源
        最近更新 更多