2

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

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

下次使用该连接时,代码永远不会返回,直到它收到“连接超时”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])--我的已替换查询以将其发布到 SO [EL 配置]:2012-07-13 18:40:10.229--ServerSession(309463268)--Connection(69352859)--Thread(Thread[http-bio-8080-exec-5, 5,main])--断开[EL Info]: 2012-07-13 18:40:10.23--UnitOfWork(1062365884)--Thread(Thread[http-bio-8080-exec-5,5,main]) --尝试在事务之外执行读取查询时检测到通信失败。正在尝试重试查询。错误为:异常 [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504):org.eclipse.persistence.exceptions.DatabaseException 内部异常:java.sql.SQLException:Eccezione IO:连接超时

我已经在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,我不会遇到这个问题,但是为了与同一服务器上的其他 webapp 保持一致,我更愿意留在 Tomcat 上。

4

1 回答 1

3

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

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

当你调用它时,它最终会委托给EclipseLink 实现Persistence.createEntityManagerFactory的接口的这个方法:PersistenceProvider

EntityManagerFactory    createEntityManagerFactory(String emName, Map map) 

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

所以这就是为什么这“在 GF 中有效”。当您让容器EntityManagerFactory通过注入或查找它来为您创建时,容器在PersistenceProviderEclipseLink 实现的接口上使用不同的方法:

EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) 

总而言之,这PersistenceUnitInfo是一个容器实现的接口,上面有这两个非常关键的方法:

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

在这种模式下,EclipseLink 不会尝试进行自己的连接处理,而是简单地调用这些方法DataSource从容器中获取连接。这确实是您所需要的。

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

  • 您可以尝试自己实例化 EclipseLinkPersistenceProvider实现,并调用createContainerEntityManagerFactory传递您自己的PersistenceUnitInfo接口实现的方法,并以这种方式将 DBCP 配置的DataSource实例提供给 EclipseLink。您需要自己解析persistence.xml文件并通过PersistenceUnitInfo. EclipseLink 也可能期望一个TransactionManager,在这种情况下,除非您找到可以添加到 Tomcat 的 TransactionManager,否则您将被卡住。

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

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

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

如果没有随附的解释,我不确定这些幻灯片会有多大用处,但本演示文稿的第二部分深入探讨了容器管理的 EntityManager 的工作方式,特别是关于连接处理和事务。您可以忽略事务部分,因为您没有使用它们并且已经在生产中,您不太可能进行显着更改,但它可能对未来的开发很有趣。

祝你好运!

于 2012-07-20T15:20:21.757 回答