2012-03-31 12 views
15

Ho bisogno di un modo per specificare dinamicamente l'unità di persistenza in un EJB.Nome unità di persistenza dinamica JPA

esempio semplificato:

Ho un'applicazione che utilizza più database come archivi di dati. Ciascuno degli archivi dati è strutturalmente uguale. A seconda del client che si connette all'applicazione ho bisogno di accedere ai dati da un archivio dati specifico.

Pertanto, vorrei utilizzare lo stesso EJB in modo che la logica aziendale non sia duplicata, , ma è sufficiente selezionare l'unità di persistenza corretta in base al client.

Fino a questo punto ho solo iniettato direttamente il gestore di entità con il nome dell'unità di persistenza codificato. Esiste un modo per iniettare dinamicamente il gestore di entità con l'unità di persistenza richiesta collegata all'EJB? Inoltre, le unità di persistenza possono essere aggiunte dinamicamente durante il runtime? Attualmente devo specificare l'unità di persistenza nel file persistence.xml. Idealmente mi piacerebbe creare pool sul server jdbc/db1, jdbc/db2 etc come richiesto mentre il sistema è in esecuzione. Quindi aggiungili al database del client centrale e collegalo a un client, in modo che quando il client si connette, controllerà il nome del pool e lo userà quando chiamerà l'EJB per ottenere l'unità di persistenza.

Sono ancora molto nuovo nello sviluppo di Java EE. Qualsiasi aiuto sarebbe molto apprezzato.

risposta

8

Nella versione JPA corrente, sfortunatamente non è possibile creare unità di persistenza in modo dinamico. Se questa funzionalità è importante per voi, si potrebbe prendere in considerazione la creazione di un problema di JIRA per essa al JPA issue tracker: http://java.net/jira/browse/JPA_SPEC

Utilizzando la @PersistenceContext annotazione, non è anche possibile selezionare dinamicamente un'unità di persistenza specifica. Questo è in realtà il dominio di sharding, che Hibernate ha provato una volta ad affrontare ma poi improvvisamente interrotto. Vedi http://www.hibernate.org/subprojects/shards.html

Ci sono comunque alcune cose che potresti fare per ottenere un effetto simile.

Un approccio consiste nel creare un bean factory EJB/CDI stateless, da iniettare con tutti i gestori di entità. Il costo di questo è marginale, dal momento che tali bean saranno raggruppati e i gestori di entità non sono così costosi da creare in primo luogo.

Se si desidera iniettarli in base a qualche condizione, questa condizione dovrà essere disponibile dal contesto o deve essere specificata nel punto di iniezione (ma se lo si farebbe, si potrebbe anche iniettare il giusto gestore di entità direttamente).

Un esempio di kickoff:

@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; 
     } 
    } 
} 

E poi nel vostro fagioli:

@Stateless 
public class SomeBean { 

    @Inject @ShardedPersistenceContext 
    private EntityManager entityManager; 

    // ... 
} 

Nota che si avrebbe bisogno Seam Persistenza qui per la @TransactionScoped annotazione. Potrebbe anche essere più semplice, ma un po 'più prolisso, dimenticare di iniettare il gestore di entità in modo trasparente e iniettare lo ShardingEntityManagerFactory invece e ottenere quello giusto da esso manualmente.

+0

Questo è quasi ciò di cui ho bisogno, ma devo ancora definire in anticipo ciascuna unità di persistenza in SharedEntityManagerFactory. Mi sarebbe piaciuto essere in grado di aggiungerli dinamicamente in modo che non siano richieste modifiche al codice e nessuna ridistribuzione richiesta. Posso semplicemente salvare i dettagli dell'unità di persistenza nel database principale e collegarlo al client quando richiesto. Ma questo è già molto meglio che implementare ogni data store come servizio web separato. Molto meno spese generali. Il servizio web ha il vantaggio di essere in grado di memorizzare l'URL in db e di utilizzarlo in modo dinamico. Dovrò salire le opzioni. – likenoother

3

Questo probabilmente non è di alcun aiuto per risolvere il problema, ma come si può sapere che questo tipo di problemi è in discussione per l'attuazione del JPA 2.1

Questo suona come uno di quei casi di multi-tenancy:

Proposal for Multitenancy Support in JPA 2.1 JSR-338

+0

Penso che sia tecnico non molto lontano dalla multitenance, ma considerando che un singolo DB è diviso in parti uguali e condiviso tra più titolari, qui un tenant vuole più DB che appaiono come uno solo. –

+0

@ArjanTijms è corretto. Quello che sto cercando è simile alla multitenancy, ma invece di usare un database, mi piacerebbe usare più che sul database. Forse vale la pena separare ciascun database in distribuzioni EJB distinte e quindi collegarsi a quest'ultimo tramite il servizio web. Non mi piace il sovraccarico, ma almeno mi dà la libertà di supportare database su sistemi diversi, se necessario. Archiviare i client su macchine diverse. Non sono sicuro se le interfacce remote possano essere utilizzate per questo? – likenoother

+0

@likeno altro buon pensiero! Sì, l'unico modo dinamico per aggiungere unità di persistenza a un sistema in esecuzione consiste nell'implementare a caldo i moduli EJB (ejb jars) in un AS. Cercare i bean da quelle che sono tecnicamente "diverse applicazioni sullo stesso AS" tramite un'interfaccia locale non è specificato, ma in realtà funziona nella pratica. Invece di iniettare direttamente questi bean, è possibile utilizzare ricerche JNDI programmatiche per ottenere in modo dinamico i bean che incapsulano quelle unità persistenti aggiunte dinamicamente. La configurazione è un po 'non ortodossa, ma potrebbe funzionare. –

1

Solo un'idea, non è sicuro se ti aiuto: si può aprire un inputStream di leggere il file persistence.xml e sostituire queste righe:

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

Poi splash Uno schermo di configurazione di connessione in cui l'utente accede al sistema, e impostare la connessione in base ai privilegi utente su quel file, quindi avviare l'applicazione principale

Non sono sicuro se questo aiuterà, è solo e idea. Dipende dall'applicazione e dalla logica aziendale.

2

È possibile utilizzare la stessa unità di persistenza per questo. È sufficiente fornire una proprietà Map quando si chiama createEntityManagerFactory() con l'url/datasource che si desidera utilizzare.

+0

Bella idea, ma non funziona in Un ambiente Java EE lo fa? È possibile iniettare un em factory, ma il metodo di creazione è solo per ambienti Java SE. –

Problemi correlati