【发布时间】:2011-07-03 12:22:31
【问题描述】:
在标准 EJB 3 中,当注入实体管理器时,持久性单元(指数据源)被硬编码到注解中:(或 xml 文件)
@PersistenceContext(unitName = "myunit")
private EntityManager entityManager;
有没有办法使用实体管理器,但在运行时按名称选择数据源?
【问题讨论】:
标签: jpa persistence ejb-3.0 entitymanager
在标准 EJB 3 中,当注入实体管理器时,持久性单元(指数据源)被硬编码到注解中:(或 xml 文件)
@PersistenceContext(unitName = "myunit")
private EntityManager entityManager;
有没有办法使用实体管理器,但在运行时按名称选择数据源?
【问题讨论】:
标签: jpa persistence ejb-3.0 entitymanager
使用 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”
【讨论】:
<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();
//---
createEntityManagerFactory(persistenceUnitName,propertiesMap);
这将使用给定的属性为命名的持久性单元创建并返回一个 EntityManagerFactory。因此,您可以在运行时相应地更改属性。
【讨论】:
有可能!我已经完成了,它可以在 JBoss AS 和 WebSphere 下运行。
我使用了一个自定义的持久化提供程序,它扩展了org.hibernate.ejb.HibernatePersistence(你需要modify a private static final field 将你的持久化提供程序名称设置为org.hibernate.ejb3.Ejb3Configuration.IMPLEMENTATION_NAME:这是一种黑魔法,但它有效)。确保您的persistence.xml 的持久性单元在<provider> 标记中设置了自定义提供程序,并且您的自定义提供程序已在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);
}
注意我这里只写了核心代码。事实上,我的持久化提供程序还有很多其他功能:
【讨论】:
我想指出的用法
Persistence.createEntityManagerFactory(persistenceUnitName)
在 Nayan 的回答中推荐按 JPA 规范 (JSR 317) 分类如下(“9.2 Java SE 环境中的引导”中的脚注):
“Java EE 容器可能支持使用这些 Java SE 引导 API;但是,不需要支持此类使用。”
所以这不是 EJB 的标准解决方案。无论如何,我可以确认这在 EclipseLink 中有效。
注意:我还不能发表评论。
【讨论】: