jpa - 如何将 Apache-Commons DBCP 与 EclipseLink JPA 和 Tomcat 7.x 结合使用

标签 jpa eclipselink apache-commons-dbcp

我一直在开发一个部署在 Tomcat 7 上的 Web 应用程序,该应用程序使用 EclipseLink JPA 来处理持久层。

在测试环境中一切正常,但由于防火墙切断了不活动的连接,我们在生产环境中遇到了严重的问题。基本上,如果连接在一段时间内处于非事件状态,Tomcat 服务器和数据库服务器之间的防火墙就会杀死它,从而在池中留下“陈旧”连接。

下次使用该连接时,代码永远不会返回,直到收到“连接超时”SQLException(下面是完整的 ex.getMessage())。

EL Fine]: 2012-07-13 18:24:39.479--ServerSession(309463268)--Connection(69352859)--Thread(Thread[http-bio-8080-exec-5,5,main])-- MY QUERY REPLACED TO POST IT TO SO [EL Config]: 2012-07-13 18:40:10.229--ServerSession(309463268)--Connection(69352859)--Thread(Thread[http-bio-8080-exec-5,5,main])--disconnect [EL Info]: 2012-07-13 18:40:10.23--UnitOfWork(1062365884)--Thread(Thread[http-bio-8080-exec-5,5,main])--Communication failure detected when attempting to perform read query outside of a transaction. Attempting to retry query. Error was: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLException: Eccezione IO: Connection timed out

我已经在 persistence.xml 中尝试了几种配置,但由于我无法访问防火墙配置,所以我对这些方法没有运气。我还尝试使用 setCheckConnections()

ConnectionPool cp = ((JpaEntityManager)em).getServerSession().getDefaultConnectionPool();
        cp.setCheckConnections();
        cp.releaseConnection(cp.acquireConnection());

我设法使用 testOnBorrow、testWhileIdle 以及 DBCP Apache Commons 提供的其他功能在测试脚本中解决了该问题。我想知道如何重写 EclipseLink 内部连接池以使用自定义连接池,以便我可以提供一个基于 DBCP 的已配置池,而不仅仅是使用 persistence.xml 配置内部连接池。

我知道我应该提供一个 SessionCustomizer,但我不确定哪个是正确的模式。基本上我想以类似 JPA 的方式保留 DBCP 的性能。

我正在 Tomcat 7 上部署,我知道如果我切换到 GF,我不会遇到这个问题,但为了与同一服务器上的其他 Web 应用程序保持一致,我宁愿留在 Tomcat 上。

最佳答案

您想要的绝对是可能的,但您可能会遇到“自己动手”方法的极限。

这是最难解释的事情之一,但是有两种有效的方法可以配置您的 EntityManagerFactory 。 “自己动手”方法和“容器”方法。

当您调用Persistence.createEntityManagerFactory时它最终委托(delegate)给 PersistenceProvider 的这个方法EclipseLink实现的接口(interface):

EntityManagerFactory    createEntityManagerFactory(String emName, Map map) 

这里的问题是 EclipseLink 将自行完成所有工作,包括它自己的连接创建和处理。这就是“自己动手”的方法。我对 EclipseLink 不太了解,不知道是否有办法使用这种方法为其提供连接。在 Stackoverflow 上呆了两天后,似乎其他人也没有这些信息。

这就是为什么“在 GF 中有效”的原因。当你让容器创建 EntityManagerFactory通过注入(inject)或查找它,容器在 PersistenceProvider 上使用不同的方法。 EclipseLink实现的接口(interface):

EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) 

总而言之,这个PersistenceUnitInfo是容器实现的接口(interface),并且具有以下两个非常关键的方法:

public DataSource getJtaDataSource();
public DataSource getNonJtaDataSource();

使用此模式,EclipseLink 将不会尝试执行自己的连接处理,而只会调用这些方法来获取 DataSource从容器中。这确实是您所需要的。

您可以采取两种可能的方法来解决此问题:

  • 您可以尝试实例化 EclipseLink PersistenceProvider自行实现并调用 createContainerEntityManagerFactory方法传入您自己的 PersistenceUnitInfo 实现接口(interface)并提供已配置的 DBCP DataSource通过这种方式将实例导入 EclipseLink。您需要解析 persistence.xml将自己归档并通过 PersistenceUnitInfo 输入数据。同样,EclipseLink 也可能期望 TransactionManager ,在这种情况下,除非您找到可以添加到 Tomcat 的 TransactionManager,否则您将陷入困境。

  • 您可以使用 Tomcat 的 Java EE 6 认证版本 TomEE 。数据源在 tomee.xml 中配置,使用 DBCP 创建,完全支持您需要的所有选项,并传递到 PersistenceProvider使用所描述的createContainerEntityManagerFactory称呼。然后你会得到 EntityManagerFactory通过@PersistenceUnit注入(inject)或者查找一下。

如果您确实尝试使用 TomEE,请确保您的 persistence.xml已更新为显式设置 transaction-type="RESOURCE_LOCAL"因为默认是JTA 。即使使用 JTA 是不合规的与 Persistence.createEntityManagerFactory方法,没有任何持久性提供者会提示并让您知道您做错了什么,他们将其视为 RESOURCE_LOCAL忽略架构。因此,当您将应用程序移植到实际经过认证的服务器时,它就会崩溃。

关于 TomEE 的另一个注意事项是,在当前版本中,您必须将 EclipseLink 库放在 <tomcat>/lib/ 中。目录。这是在 trunk 中修复的,只是尚未发布。

如果没有附带的解释,我不确定这些幻灯片会有多大用处,但本演示文稿的第二部分将深入探讨 how container-managed EntityManager's work, specifically with regards to connection handling and transactions 。您可以忽略事务部分,因为您没有使用它们并且已经在生产中,您不太可能进行显着更改,但它对于 future 的开发可能很有趣。

祝你好运!

关于jpa - 如何将 Apache-Commons DBCP 与 EclipseLink JPA 和 Tomcat 7.x 结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11526654/

相关文章:

java - 使用 Spring Data JPA 访问数据时,会在任一表上创建空列

java - 在实体管理器上调用合并后实体丢失状态

java - 查询运行缓慢

java - JPA 持久保存与现有实体有关系的新实体

java - Apache dbcp 连接池无法正常工作

java - 批量更新缓存问题? hibernate/集成测试/内存数据库/

java - 当一对多关系的火合并时,Hibernate 抛出 EntityNotFoundException

java - JPA - 为什么使用@JoinColumn?

java - 停止 Java 时不执行存储过程