JPA动态持久化单元名称

时间:2012-03-31 15:26:48

标签: java-ee jpa ejb-3.1

我需要一种在EJB中动态指定持久性单元的方法。

简化示例:

我有一个使用多个数据库作为数据存储的应用程序。 每个数据存储在结构上都是相同的。 根据连接到应用程序的客户端,我需要从中访问数据 特定的数据存储。

因此,我想使用相同的EJB,以便不重复业务逻辑, 但是然后只需根据客户端选择正确的持久性单元。

到目前为止,我只是直接向实体管理器注入硬编码的持久性单元名称。 有没有办法可以动态地为实体管理器注入附加到EJB的所需持久性单元? 此外,可以在运行时动态添加持久性单元吗? 我目前必须在persistence.xml文件中指定持久性单元。 理想情况下,我想在系统运行时根据需要在服务器jdbc / db1,jdbc / db2等上创建池。然后将这些添加到中央客户端数据库并将其链接到客户端,以便在客户端连接时,它将检查池的名称,并在调用EJB以获取持久性单元时使用它。

我还是Java EE开发的新手。任何帮助将不胜感激。

4 个答案:

答案 0 :(得分:8)

在当前的JPA版本中,遗憾的是无法动态创建持久性单元。如果此功能对您很重要,您可以考虑在JPA问题跟踪器上为其创建JIRA问题:http://java.net/jira/browse/JPA_SPEC

使用@PersistenceContext注释,也无法动态选择特定的持久性单元。这实际上是分片的领域,Hibernate曾试图解决但后来突然停止了。见http://www.hibernate.org/subprojects/shards.html

但是你可以采取一些措施来获得类似的效果。

一种方法是创建一个无状态EJB / CDI工厂bean,并向所有实体管理器注入。这个成本是微不足道的,因为这些bean将被合并,实体经理首先创建的成本并不高。

如果你还想根据某些条件注入它们,那么这个条件必须从上下文中获得,或者必须在注入点指定(但如果你这样做,你也可以注入正确的实体经理直接)。

启动示例:

@Stateless
@TransactionAttribute(SUPPORTS)
public class ShardingEntityManagerFactory {

    @Resource
    private SessionContext sessionContext;

    @PersistenceContext(unitName = "pu1")
    private EntityManager entityManager1;

    @PersistenceContext(unitName = "pu2")
    private EntityManager entityManager2;

    @Produces @TransactionScoped @ShardedPersistenceContext
    public EntityManager getEntityManager() {
        if (sessionContext.isCallerInRole("FOO")) {
            return entityManager1;
        } else {
            return entityManager2;
        }
    }
}

然后在你的豆子里:

@Stateless
public class SomeBean {

    @Inject @ShardedPersistenceContext
    private EntityManager entityManager;

    // ...
}

请注意,此处需要Seam Persistence用于@TransactionScoped注释。忘记透明地注入实体管理器并注入ShardingEntityManagerFactory并手动从中获取正确的实体管理器也可能更容易,但更冗长一点。

答案 1 :(得分:3)

这可能对解决问题没有帮助,但您可能想知道正在讨论这种问题以实施JPA 2.1

这听起来像是多租户案例之一:

Proposal for Multitenancy Support in JPA 2.1 JSR-338

答案 2 :(得分:2)

您可以使用相同的持久性单元。当您使用要使用的url / datasource调用createEntityManagerFactory()时,您只需要提供属性Map。

答案 3 :(得分:1)

只是一个想法,不确定它是否会有所帮助: 您可以打开一个inputStream来读取persistence.xml文件并替换这些行:

<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/agendasccv2?zeroDateTimeBehavior=convertToNull"/>
<property name="javax.persistence.jdbc.password" value="btxbtxbtx"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>

然后启动用户登录的连接配置屏幕,并根据该文件的用户权限设置连接,然后启动主应用程序

不确定这是否会有所帮助,这只是一个想法。 取决于您的应用程序和业务逻辑。