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

相關問題