2012-03-31 7 views
15

Ich benötige eine Möglichkeit, die Persistenzeinheit in einem EJB dynamisch anzugeben.Name der dynamischen JPA-Persistenzeinheit

Vereinfachtes Beispiel:

Ich habe eine Anwendung mehrere Datenbanken als Datenspeicher verwenden. Jeder der Datenspeicher ist strukturell identisch. Je nachdem, welcher Client mit der Anwendung verbunden ist, muss ich auf die Daten von einen bestimmten Datenspeicher zugreifen.

Daher würde ich gerne die gleiche EJB verwenden, so dass die Geschäftslogik nicht dupliziert wird, , aber dann wählen Sie einfach die richtige Persistenzeinheit basierend auf dem Client.

Bis zu diesem Punkt habe ich den Entity Manager nur direkt mit dem harten Namen der Persistenzeinheit injiziert. Gibt es eine Möglichkeit, den Entity Manager dynamisch mit der erforderlichen Persistenzeinheit, die an das EJB angeschlossen ist, zu injizieren? Können Persistenzeinheiten zur Laufzeit dynamisch hinzugefügt werden? Ich muss derzeit die Persistenzeinheit in der Datei persistence.xml angeben. Idealerweise möchte ich Pools auf dem Server jdbc/db1, jdbc/db2 usw. während der Ausführung des Systems erstellen. Dann fügen Sie einfach diese an die zentrale Kundendatenbank und verknüpfen es mit einem Client, so dass, wenn der Client eine Verbindung herstellt, wird es den Namen des Pools überprüfen, und es verwenden, wenn die EJB-Aufruf der Persistenzeinheit zu bekommen.

Ich bin immer noch wirklich neu in Java EE Entwicklung. Jede Hilfe würde sehr geschätzt werden.

Antwort

8

In der aktuellen JPA-Version ist es leider nicht möglich, Persistenzeinheiten dynamisch zu erstellen. Wenn diese Funktionalität es für Sie wichtig ist, könnten Sie ein JIRA Problem für sie an der JPA issue tracker erwägen sollten: http://java.net/jira/browse/JPA_SPEC

Mit der @PersistenceContext Anmerkung, ist es auch nicht möglich, dynamisch eine bestimmte Persistenzeinheit zu wählen. Dies ist eigentlich die Domäne des Shardings, die Hibernate einmal zu lösen versuchte, dann aber plötzlich abbrach. Siehe http://www.hibernate.org/subprojects/shards.html

Es gibt jedoch ein paar, was man tun könnte einen ähnlichen Effekt zu erhalten.

Ein Ansatz ist es, eine Stateless EJB/CDI Fabrik Bohne zu erstellen, dass Sie mit allen Ihren Entity-Manager injizieren. Die Kosten hierfür sind marginal, da diese Bohnen gepoolt werden und Entity-Manager sind nicht so teuer in erster Linie geschaffen werden.

Wenn Sie sie auch basierend auf einer Bedingung injizieren möchten, muss diese Bedingung aus dem Kontext verfügbar sein oder muss am Injektionspunkt angegeben werden (aber wenn Sie das tun würden, könnten Sie genauso gut injizieren der richtige Entity Manager direkt).

Ein Kickoff Beispiel:

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

Und dann in Ihre Bohnen:

@Stateless 
public class SomeBean { 

    @Inject @ShardedPersistenceContext 
    private EntityManager entityManager; 

    // ... 
} 

Beachten Sie, dass hier Seam Persistence für die @TransactionScoped Anmerkung benötigen würde. Es könnte auch einfacher sein, aber ein wenig ausführlicher, um das Einspritzen des Entity-Manager transparent und injizieren die ShardingEntityManagerFactory statt und erhalten das Recht, eine von ihm manuell zu vergessen.

+0

Dies ist fast das, was ich brauche, aber ich muss immer noch jede Persistenz-Einheit im Voraus in der SharedEntityManagerFactory definieren. Ich hätte gerne in der Lage sein, sie dynamisch hinzuzufügen, so dass keine Codeänderungen erforderlich sind und keine erneute Implementierung erforderlich ist. Ich kann einfach die Details der Persistenzeinheit in der Hauptdatenbank speichern und bei Bedarf mit dem Client verknüpfen. Aber das ist schon viel besser, als jeden Datenspeicher als separaten Webservice zu implementieren. Viel weniger Aufwand. Der Webservice hat den zusätzlichen Vorteil, dass er die URL in db speichern und dynamisch verwenden kann. Ich muss die Optionen aufheben. – likenoother

3

Dies ist wahrscheinlich nicht helfen, das Problem zu lösen, aber Sie können wissen, dass diese Art von Problemen bei der Umsetzung von JPA 2.1

Dies diskutiert wird klingt wie einer jener Fälle von Multitenancy:

Proposal for Multitenancy Support in JPA 2.1 JSR-338

+0

Ich denke, es ist technisch nicht so weit weg von Multitenancy in der Tat, aber während dort eine einzige DB in gleiche Anteile geteilt und zwischen mehreren Mietern geteilt wird, will hier ein einziger Mieter mehrere DBs erscheinen als eine einzige. –

+0

@ArjanTijms ist korrekt. Was ich suche ist ähnlich wie Multi-Tenant, aber anstatt eine Datenbank zu verwenden, würde ich gerne mehr als auf Datenbank verwenden. Vielleicht lohnt es sich, jede Datenbank in separate EJB-Bereitstellungen zu trennen und dann über einen Webdienst eine Verbindung herzustellen. Ich mag den Overhead nicht, aber zumindest gibt es mir die Freiheit, Datenbanken bei Bedarf auf verschiedenen Systemen zu unterstützen. Speichern Sie Clients auf verschiedenen Computern. Nicht sicher, ob Remote-Schnittstellen dafür verwendet werden können? – likenoother

+0

@likenoother gutes Denken! Ja, der einzige dynamische Weg zum Hinzufügen von Persistenzeinheiten zu einem laufenden System besteht in der Bereitstellung von EJB-Modulen (ejb jars) auf einem AS. Das Nachschlagen von Beans von technisch verschiedenen Anwendungen auf derselben AS über eine lokale Schnittstelle ist nicht spezifiziert, aber tatsächlich funktioniert es in der Praxis. Anstatt diese Beans direkt zu injizieren, können Sie programmgesteuerte JNDI-Lookups verwenden, um dynamisch Beans zu erhalten, die diese dynamisch hinzugefügten persistenten Einheiten einkapseln. Das Setup ist ein wenig unorthodox, aber es könnte einfach funktionieren. –

1

Nur eine Idee, wenn es nicht sicher helfen werden: Sie eine input öffnen kann die persistence.xml Datei und ersetzen Sie diese Zeilen zu lesen:

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

dann eine Verbindung Config-Bildschirm spritzen, wo der Benutzer anmeldet, und stellen Sie die Verbindung entsprechend der Benutzerrechte auf die Datei, dann die Hauptanwendung

Nicht sicher gestartet werden, wenn das helfen wird, es ist nur und Idee. Hängt von Ihrer Anwendung und Geschäftslogik ab.

2

Sie können hierfür die gleiche Persistenzeinheit verwenden. Sie müssen nur eine Eigenschaftenzuordnung bereitstellen, wenn Sie createEntityManagerFactory() mit der URL/Datenquelle aufrufen, die Sie verwenden möchten.

+0

Nette Idee, aber das funktioniert nicht in Eine Java EE-Umgebung tut es? Sie können eine em factory injizieren, aber die create-Methode ist nur für Java SE-Umgebungen vorgesehen. –