我一直在开发一个部署在 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)并提供已配置的 DBCPDataSource
通过这种方式将实例导入 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/