2012-03-31 108 views
15

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

简化的例子:

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

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

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

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

回答

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; 

    // ... 
} 

请注意,您将需要缝持久这里为@TransactionScoped注释。它也可能更容易,但更冗长一点,忘记注入实体管理器透明,并注入ShardingEntityManagerFactory而不是手动从它获得正确的。

+0

这几乎是我所需要的,但我仍然需要将每个持久性单元预先定义到SharedEntityManagerFactory中。 我希望能够动态添加它们,以便不需要更改代码,也不需要重新部署。我可以简单地将持久性单元的详细信息保存在主数据库中,并在需要时将其链接到客户端。但是,这比将每个数据存储实现为单独的Web服务要好得多。更少的开销。 Web服务具有能够将URL存储在db中并动态使用它的附加好处。我将不得不提出选择。 – likenoother 2012-04-01 08:31:50

3

这可能是无助于解决问题,但你可能想知道,这种问题正为JPA 2.1

讨论了执行这听起来像的多租户的这些情况之一:

Proposal for Multitenancy Support in JPA 2.1 JSR-338

+0

我认为它的技术与多租户服务相差不远,但是,尽管单个数据库被分成平等份额并在多个租户之间共享,但这里的单租户希望多个数据库显示为单一数据库。 – 2012-03-31 19:31:44

+0

@ArjanTijms是正确的。我正在寻找的是类似于多租户,但不是使用一个数据库,我想使用多于数据库。也许值得将每个数据库分离成单独的EJB部署,然后通过Web服务连接。我不喜欢这种开销,但至少它可以让我在需要的时候支持跨不同系统的数据库。 将客户端存储在不同的机器上。不确定是否可以使用远程接口? – likenoother 2012-04-01 08:36:24

+0

@likenoother善于思考!是的,将持久性单元添加到正在运行的系统的一种动态方法是将EJB模块(ejb jars)热部署到AS。通过本地接口查看技术上“同一AS上不同的应用程序”中的bean是未指定的,但实际上在实践中起作用。与其直接注入这些bean,您可以使用编程式JNDI查找来动态获取封装这些动态添加的持久单元的bean。虽然这个设置有点不正常,但它可能正常工作。 – 2012-04-01 11:51:31

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"/> 

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

不知道这会有所帮助,它只是和想法。 取决于您的应用程序和业务逻辑。

2

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

+0

不错的主意,但是这在 中不起作用Java EE环境能做到吗?您可以注入em工厂,但create方法仅适用于Java SE环境。 – 2012-10-03 08:30:59