使用JPA/EclipseLink/EJB从Java web应用程序访问多个数据库
我已经构建了一个简单的使用JPA/EclipseLink/EJB从Java web应用程序访问多个数据库,java,jakarta-ee,jpa,glassfish,eclipselink,Java,Jakarta Ee,Jpa,Glassfish,Eclipselink,我已经构建了一个简单的SOAPjava应用程序(服务器端),我正在使用Glassfish4、JPA/EclipseLink、EJB。我已经在Glassfish中设置了db连接(资源/池)。请提供一些设计模式/知识,以利用单个应用程序中的多个数据库。创建多个持久性单元是多址访问的好主意吗??或者是否有其他优化的解决方案? 我有一个数据库访问的通用类 public class GenericDAO<T> { /* * private static final EntityManager
SOAP
java应用程序(服务器端),我正在使用Glassfish4、JPA/EclipseLink、EJB
。我已经在Glassfish中设置了db连接(资源/池)。请提供一些设计模式/知识,以利用单个应用程序中的多个数据库。创建多个持久性单元是多址访问的好主意吗??或者是否有其他优化的解决方案?
我有一个数据库访问的通用类
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();
}
}
请在此文件中提出一些优化/更改建议
我一直在使用EJB
来利用泛型类。
例如:
@无状态
公共类MemberEJB扩展了GenericDAO{
/**
*@see GenericDAO#GenericDAO(类)
*/
公共成员EJB(){
super(memregmentity.class);
//TODO自动生成的构造函数存根
}
公共列表getListOfMemberByName(字符串名称){
映射参数=新的HashMap();
参数.put(“memName”,name+'%');
返回super.findAllWithGivenCondition(“Mem.getMemberByName”,参数);
}
}
客户端应用程序提供要使用的数据库名称,并且每个数据库都具有相同的结构。我只需要根据客户的要求访问多个数据库。当处理一个应用程序和多个DBs时,EclipseLink提供两种解决方案。哪一个更适合您取决于您的用例,如果 用户需要将多个持久性单元映射为单个持久性单元 应用程序中的持久性上下文 看看 如果你是这样的话 多个应用程序客户端必须共享数据源,并具有专用 访问他们的数据 环境 而不是看一眼 或者,这描述了一种设计多租户的方法,无需绑定到特定于供应商的功能 关于评论的更新
我不认为您所追求的动态数据源路由类型作为glassfish的现成构造存在。但实施起来也不难。您应该看看它们提供的和参考实现。您应该能够在它的基础上编写自己的路由器,而不会出现太多问题当然,它可以用更复杂的方式完成,但我想到的还有一个简单的解决方案。如果部署尽可能多的应用程序和数据库,并设计一个小型请求路由应用程序,该应用程序将通过请求中提供的“databaseId”将所有客户请求转发到相应的应用程序,会怎么样。此解决方案在分布式环境中非常有效。我的解决方案是为第二个数据库添加第二个持久性单元,然后重构GenericDAO,使EntityManager不是类的属性,而是传递给每个方法。然后,我将为每个数据库创建facade对象,将GenericDAO和相关EntityManager注入其中。如果您真的需要,可以使用一个公共接口来保持api不变。可能是这样的:
<?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="first_PU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/simfin_1</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-unit name="second_PU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/simfin_2</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>
persistence.xml
<?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-unit name="MySecondPersistenceUnit_PU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/other-jta-datasource</jta-data-source>
<class>org.demo.model.OtherEntityOne</class>
<class>org.demo.model.OtherEntityTwo</class>
<class>org.demo.model.OtherEntityThree</class>
<class>org.demo.model.OtherEntityFour</class>
</persistence-unit>
</persistence>
实体类:
public class SomeEntity implements IEntity {
....
}
DAO Facade数据库一:
public class GenericFacadeOne {
@PersistenceContext(unitName = "SavingBalanceDemoServer_PU")
private EntityManager em;
@Autowired
private GenericDao dao;
@Transactional(propogation=Propogation.REQUIRED)
public void saveSomeEntity(SomeEntity entity) {
getDao().save(getEm(), entity);
}
public void setEm(EntityManager em) {
this.em = em;
}
public EntityManager getEntityManager() {
return this.em;
}
public void setDao(GenericDao dao) {
this.em = em;
}
public GenericDao getDao() {
return this.dao;
}
}
DAO Facade数据库二:
public class GenericFacadeTwo {
@PersistenceContext(unitName = "MySecondPersistenceUnit_PU")
private EntityManager em;
@Autowired
private GenericDao dao;
@Transactional(propogation=Propogation.REQUIRED)
public void saveSomeEntity(SomeEntity entity) {
getDao().save(getEm(), entity);
}
public void setEm(EntityManager em) {
this.em = em;
}
public EntityManager getEntityManager() {
return this.em;
}
public void setDao(GenericDao dao) {
this.em = em;
}
public GenericDao getDao() {
return this.dao;
}
}
希望这是有道理的,让我知道如果你需要任何澄清 我们面对相同的用例,最终创建了多个持久化单元并构建了一个实体管理器工厂,该工厂根据客户端发送的参数返回正确的实体管理器(在我们的例子中,作为枚举,
环境
)。然后,我们不在客户机中注入持久性上下文,而是注入这个工厂并调用getEntityManager(环境)
示例枚举:
public enum Environment{
DEV, PROD
}
在您的情况下,GenericDAO将按照以下方式重构:
public class GenericDAO<T> {
@EJB
private EntityManagerFactory entityManagerFactory;
public void save(T entity, Environment env) {
entityManagerFactory.getEntityManager(env).persist(entity);
}
}
另一种解决方案是以编程方式创建持久上下文 定义一个不带连接的persistent.xml。类似于: persistent.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" ... >
<persistence-unit name="UserInfo" transaction-type="JTA">
<class>mx.saaskun.model.UserInfo</class>
</persistence-unit>
</persistence>
然后,您可以将其用作:
public class UserService{
@EJB
DynamicResource radResources;
public List<UserInfo> listAll(){
List<UserInfo allUsers = new ArrayList<>();
String[] databases = new String[]{"jndi/simfin","jndi/simfin2"};
for(String db:databases){
List results = listServerUsers("simfin", db);
allUsers.addAll(results);
}
return allUsers;
}
protected List<UserInfo> listServerUsers(String unitName, String jndi){
EntityManager em= radResources.getEntityManager(unitName,jndi);
try {
Query q = em.createNamedQuery("UserInfo.findAll");
return (List<UserInfo>) q.getResultList();
} finally {
em.close();
}
}
}
公共类用户服务{
@EJB
动态资源;
公共列表listAll(){
ListThank U for reply。我的要求是客户端请求具有数据库名称的应用程序,我应该在运行时点击所需的数据库并进行回复。即,应该为每个客户端请求动态设置数据库连接。所有数据库都是相同的,只是数据库名称不同,客户端可以有多个re同时,Web服务应该做出相应的响应。您是否要使用persistence.xml中定义的具有相同对象关系映射的多个数据库?换句话说:所有数据库都将使用相同的一组实体?是的。您说得非常对..而且可以帮上忙..我也像您一样考虑这个问题,但是应该有更有效的方法。客户端不仅发送“databaseId”,还发送其他参数。请详细说明??这很简单,但我还是添加了一些细节。我很欣赏你的解决方案,但每个数据库的实现都需要DAO Facade。请详细说明实体接口<代码>公共接口City
?由@Virgi提供的解决方案也是一个很好的解决方案。我同意@Virgi提供了一个简单而有效的解决方案。我想这取决于您的需求-我希望我的通用dao中包含所有CRUD操作,然后有一个门面将我的持久性逻辑封装到单个事务中(并提供一个可读性好的api)。另一个选项是使用单个持久性单元并动态更新其中的属性,请在此处查看此答案
public enum Environment{
DEV, PROD
}
public class GenericDAO<T> {
@EJB
private EntityManagerFactory entityManagerFactory;
public void save(T entity, Environment env) {
entityManagerFactory.getEntityManager(env).persist(entity);
}
}
<?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="first_PU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/simfin_1</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-unit name="second_PU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/simfin_2</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>
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" ... >
<persistence-unit name="UserInfo" transaction-type="JTA">
<class>mx.saaskun.model.UserInfo</class>
</persistence-unit>
</persistence>
@Stateless
@LocalBean
public class DynamicResource implements Serializable{
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public EntityManagerFactory getEntityManager(unitName, jndiConnection){
Map properties = new HashMap();
properties.put("javax.persistence.jtaDataSource", jndiConnection);
return Persistence.createEntityManagerFactory(unitName, properties);
}
}
public class UserService{
@EJB
DynamicResource radResources;
public List<UserInfo> listAll(){
List<UserInfo allUsers = new ArrayList<>();
String[] databases = new String[]{"jndi/simfin","jndi/simfin2"};
for(String db:databases){
List results = listServerUsers("simfin", db);
allUsers.addAll(results);
}
return allUsers;
}
protected List<UserInfo> listServerUsers(String unitName, String jndi){
EntityManager em= radResources.getEntityManager(unitName,jndi);
try {
Query q = em.createNamedQuery("UserInfo.findAll");
return (List<UserInfo>) q.getResultList();
} finally {
em.close();
}
}
}