Zugriff auf mehrere Datenbanken aus einer Java-web-Anwendung mit JPA/EclipseLink/EJB
Ich gebaut habe, eine einfache SOAP
java-Anwendung(server-Seite) und ich bin mit Glassfish4,JPA/EclipseLink,EJB
. Ich habe den db-verbindungen(Ressourcen/pools) in Glassfish. Schlagen Sie bitte einige design-Muster/wissen zu nutzen, um mehrere Datenbanken von einer einzigen Anwendung. Ist die Erstellung mehrerer persistence-unit eine gute Idee für mehrere access?? Oder gibt es eine andere optimale Lösung ?
Ich habe eine generische Klasse für den Datenbank-Zugriff.
public class GenericDAO<T> {
/*
* private static final EntityManagerFactory emf =
* Persistence.createEntityManagerFactory("icanPU"); private EntityManager
* em;
*/
/*
* Persistence context is injected with following @PersistenceContext
* annotation. This uses all persistence configurations as specified in the
* persistence.xml.
*
* Note this kind of injection can only be done for JTA data sources.
*/
@PersistenceContext(unitName = "SavingBalanceDemoServer_PU")
private EntityManager em;
private Class<T> entityClass;
public EntityManager getEntityManager() {
return this.em;
}
public void joinTransaction() {
/* em = emf.createEntityManager(); */
em.joinTransaction();
}
public GenericDAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
public void save(T entity) {
em.persist(entity);
}
//Added by Sudeep for bulk Insert of List object.
public void saveList(List<T> objList) {
for (Iterator<T> iterator = objList.iterator(); iterator.hasNext();) {
T t = (T) iterator.next();
em.persist(t);
}
}
public void delete(Object id, Class<T> classe) {
T entityToBeRemoved = em.getReference(classe, id);
em.remove(entityToBeRemoved);
}
public T update(T entity) {
return em.merge(entity);
}
public int truncateUsingNative(String tableName) {
Query qry = em.createNativeQuery("TRUNCATE TABLE " + tableName);
return qry.executeUpdate();
}
//Added by Sudeep for bulk Update of List object.
public void updateList(List<T> entity) {
for (Iterator<T> iterator = entity.iterator(); iterator.hasNext();) {
T t = (T) iterator.next();
em.merge(t);
}
}
public T find(int entityID) {
//em.getEntityManagerFactory().getCache().evict(entityClass, entityID);
return em.find(entityClass, entityID);
}
public T find(long entityID) {
//em.getEntityManagerFactory().getCache().evict(entityClass, entityID);
return em.find(entityClass, entityID);
}
public T find(Object compositePkObject) {
//em.getEntityManagerFactory().getCache().evict(entityClass, entityID);
return em.find(entityClass, compositePkObject);
}
public T findReferenceOnly(int entityID) {
return em.getReference(entityClass, entityID);
}
//Using the unchecked because JPA does not have a
//em.getCriteriaBuilder().createQuery()<T> method
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<T> findAll() {
CriteriaQuery cq = null;
if (isDbAccessible()) {
try {
cq = em.getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return em.createQuery(cq).getResultList();
} catch (org.eclipse.persistence.exceptions.DatabaseException ex) {
System.out.println("The zzz error is :" + ex.toString());
/*JSFMessageUtil jsfMessageUtil = new JSFMessageUtil();
jsfMessageUtil
.sendErrorMessageToUser("Database Server is unavailable or not accessible! Please, contact your system admin!");*/
return null;
}
}
return null;
}
private boolean isDbAccessible() {
return em.isOpen();
}
@SuppressWarnings("unchecked")
public List<T> findAllWithGivenCondition(String namedQuery,
Map<String, Object> parameters) {
List<T> result = null;
Query query = em.createNamedQuery(namedQuery);
if (parameters != null && !parameters.isEmpty()) {
populateQueryParameters(query, parameters);
}
result = (List<T>) query.getResultList();
return result;
}
@SuppressWarnings("unchecked")
public List<T> findAllWithGivenConditionLazyLoading(String namedQuery,
Map<String, Object> parameters,int startingAt, int maxPerPage) {
List<T> result = null;
Query query = em.createNamedQuery(namedQuery);
if (parameters != null && !parameters.isEmpty()) {
populateQueryParameters(query, parameters);
}
query.setFirstResult(startingAt);
query.setMaxResults(maxPerPage);
result = (List<T>) query.getResultList();
return result;
}
@SuppressWarnings("unchecked")
public List<T> findAllWithGivenConditionJpql(String jpql,
Map<String, Object> parameters) {
List<T> result = null;
Query query = em.createQuery(jpql);
if (parameters != null && !parameters.isEmpty()) {
populateQueryParameters(query, parameters);
}
result = (List<T>) query.getResultList();
return result;
}
@SuppressWarnings("unchecked")
public T findOneWithGivenConditionJpql(String jpql,
Map<String, Object> parameters) {
Query query = em.createQuery(jpql);
if (parameters != null && !parameters.isEmpty()) {
populateQueryParameters(query, parameters);
}
return (T) query.getSingleResult();
}
//Using the unchecked because JPA does not have a
//query.getSingleResult()<T> method
@SuppressWarnings("unchecked")
protected T findOneResult(String namedQuery, Map<String, Object> parameters) {
T result = null;
try {
if (!em.isOpen()) {
/*JSFMessageUtil jsfMessageUtil = new JSFMessageUtil();
jsfMessageUtil
.sendErrorMessageToUser("Database Server is unavailable or not accessible! Please, contact your system admin!");*/
} else {
Query query = em.createNamedQuery(namedQuery);
//Method that will populate parameters if they are passed not
//null and empty
if (parameters != null && !parameters.isEmpty()) {
populateQueryParameters(query, parameters);
}
result = (T) query.getSingleResult();
}
} catch (NoResultException e) {
//JSFMessageUtil jsfMessageUtil = new JSFMessageUtil();
//jsfMessageUtil.sendErrorMessageToUser("No Information Found...!");
//e.printStackTrace();
return null;
} catch (org.eclipse.persistence.exceptions.DatabaseException e) {
/*JSFMessageUtil jsfMessageUtil = new JSFMessageUtil();
jsfMessageUtil
.sendErrorMessageToUser("Database Server is unavailable or not accessible!");*/
e.printStackTrace();
}
return result;
}
private void populateQueryParameters(Query query,
Map<String, Object> parameters) {
for (Entry<String, Object> entry : parameters.entrySet()) {
query.setParameter(entry.getKey(), entry.getValue());
}
}
/**
* @param startingAt
* @param maxPerPage
* @param t
* @return list of persisted entities which belong to this class t
*/
@SuppressWarnings("unchecked")
public List<T> getAllLazyEntities(int startingAt, int maxPerPage, Class<T> t) {
//regular query that will search for players in the db
Query query = getEntityManager().createQuery(
"select p from " + t.getName() + " p");
query.setFirstResult(startingAt);
query.setMaxResults(maxPerPage);
return query.getResultList();
}
/**
* @param clazz
* @return count of existing entity rows from backend
*/
public int countTotalRows(Class<T> clazz) {
Query query = getEntityManager().createQuery(
"select COUNT(p) from " + clazz.getName() + " p");
Number result = (Number) query.getSingleResult();
return result.intValue();
}
/**
* @return count of existing entity rows from backend acccording to given
* condition
*/
public int countTotalRowsWithCond(Class<T> clazz, String Cond) {
Query query = getEntityManager()
.createQuery(
"select COUNT(p) from " + clazz.getName() + " p "
+ Cond + " ");
Number result = (Number) query.getSingleResult();
return result.intValue();
}
}
Dynamisch ändern unitName
im @PersistenceContext(unitName = "SavingBalanceDemoServer_PU")
eine gute Idee ? Bitte schlagen Sie mich.
Meine persistence.xml
ist :
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="SavingBalanceDemoServer_PU"
transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/simfin</jta-data-source>
<class>org.demo.model.MemRegMcgEntity</class>
<class>org.demo.model.SavAccHolderMcgEntity</class>
<class>org.demo.model.SavAccMcgEntity</class>
<class>org.demo.model.SavTransactionEntity</class>
</persistence-unit>
</persistence>
Bitte vorschlagen, einige Optimierungen/Veränderungen in dieser Datei.
Und ich habe mit EJB
um die Verwendung der Generischen Klasse.
eg:
@Stateless
public class MemberEJB extends GenericDAO<MemRegMcgEntity> {
/**
* @see GenericDAO#GenericDAO(Class<T>)
*/
public MemberEJB() {
super(MemRegMcgEntity.class);
//TODO Auto-generated constructor stub
}
public List<MemRegMcgEntity> getListOfMemberByName(String name){
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("memName", name+'%');
return super.findAllWithGivenCondition("Mem.getMemberByName", parameters);
}
}
Die client-app bietet den Datenbank-Namen zu verwenden, und jede Datenbank hat dieselbe Struktur. Ich brauche einfach nur den Zugriff auf mehrere Datenbank entsprechend client ' s Anforderung.
- Verwenden Sie mehrere Datenbanken mit dem gleichen Objekt-relationale-mapping, wie in der persistence.xml? In anderen Worten: wird die gleiche Menge von Entitäten verwendet werden, die für alle Datenbanken?
- Ja. Sie sind sehr viel richtig..
- forum.Frühling.io/forum/Feder-Projekte/data/... und stackoverflow.com/questions/10674051/... helfen könnte..
Du musst angemeldet sein, um einen Kommentar abzugeben.
Standen wir vor der gleichen use-case-und landete mehrere Persistenz-unit und Aufbau eines entity-manager-factory, das gibt das richtige entity-manager laut einem parameter vom client gesendet (als enum in unserem Fall
Environment
). Dann, anstatt die Injektion der Persistenz-Kontext, in dem Kunden, Spritzen wir dieses Werk und nennengetEntityManager(environment)
.Beispiel enum:
In Ihrem Fall, die GenericDAO würde umgestaltet werden auf diese Weise:
- Und dann Ihren AUFTRAGGEBER nennen würde, mit
dao.save(someEntity, Environment.DEV)
.Ihre persistence.xml würde so enden:
Beim Umgang mit einer app und mehrere DBs bildet EclipseLink zwei Lösungen. Welche ist besser geeignet für Sie hängt von Ihrem Anwendungsfall, wenn
Werfen Sie einen Blick auf Verwendung Mehrerer Datenbanken mit einer Composite-Persistence-Unit
Wenn Ihr Fall ist, dass
als nehmen einen Blick auf Tenant-Isolation Mit EclipseLink
Alternativ, diese blog-post beschreibt eine Möglichkeit der Gestaltung einer multi-tenancy, ohne Bindung an Lieferanten, die bestimmte Funktionen
UPDATE in Bezug auf den Kommentar
Ich glaube nicht, dass der Typ von dynamic data source routing, dass Sie nach den vorhanden wie eine fertige Konstrukt von glassfish. Aber es sollte nicht allzu schwer sein es zu implementieren, entweder. Sie sollten einen Blick auf die TomEE dynamische datasource-api und die Referenz-Implementierung, die Sie zur Verfügung gestellt. Sie sollten in der Lage sein zu schreiben Sie Ihre eigenen router basierend auf, ohne zu viel Probleme
Wäre meine Lösung, um eine zweite Ausdauer-Einheit für die zweite Datenbank, dann umgestalten von GenericDAO, so dass der EntityManager ist nicht ein Attribut der Klasse, aber gingen in jeder Methode. Ich würde Sie dann erstellen, die Fassade der Objekte für jede Ihrer Datenbanken, mit denen die GenericDAO und die entsprechende EntityManager injiziert Ihnen. Wenn Sie wirklich wollten, könnten Sie haben eine gemeinsame Schnittstelle zu halten, die api die gleichen. Es könnte wie folgt Aussehen:
persistence.xml
Generische DAO:
Entity-Schnittstelle:
Entity-Klasse:
DAO Fassade Datenbank Ein:
DAO Fassade Datenbank Zwei:
Hoffentlich das macht Sinn, lassen Sie mich wissen, wenn Sie Erläuterungen benötigen!
public Interface IEntity
? Lösung geben, die von @Virgi ist auch gut.Sicher es kann getan werden, mehr anspruchsvolle Art und Weise, aber es ist auch einfach eine Lösung, die kommt mir in den Sinn. Was ist, wenn Sie bereitstellen, so viele Anwendungen wie viele Datenbanken du hast und design eine kleine Anfrage-routing-Anwendung, die alle Ihre clientrequests den entsprechenden app 'databaseId' in der Anfrage. Diese Lösung wird in einer verteilten Umgebung.
Andere Lösung ist das erstellen der persistenten Kontext programmatisch.
Definieren persistent.xml ohne die Verbindung. Ähnlich:
persistent.xml
Erstellen Sie eine Fabrik für die benutzerdefinierte Verbindung:
Die Methode erhält zwei Parameter, die benutzerdefinierte Einheit-name und JNDI für die Verbindung.
DynamicResource.java
dann verwenden Sie als: