为什么同时使用 C3PO 和 JNDI 会产生问题?

Why does using C3PO and JNDI at the same time create an issue?

我是 运行 一个使用 Tomcat 8 服务器中的 Hibernate 的应用程序。我想使用不同于 Hibernate 的连接池,因为它们很明显不适合在生产环境中使用。

另外,他们提到:

For use inside an application server, you should almost always configure Hibernate to obtain connections from an application server javax.sql.Datasource registered in JNDI.

看来我需要做两件事:

  1. 配置 Hibernate 以使用第三方连接池 -- Hibernate 推荐 C3PO
  2. 配置 Hibernate 以从在 JNDI
  3. 中注册的 javax.sql.Datasource 对象获取连接

我一直在研究如何进行这些更改,我遇到了 this SO 问题。发布者已经在使用 C3PO 并询问如何通过 JNDI Datasource 对象连接到他们的数据库。但是,他们 运行 遇到了问题,因为他们在按照回答者的步骤使用 JNDI 数据源时已经在使用 C3PO。发布者在接受的答案的评论部分说:

yeah right, I've been doing sth really silly in there using both c3p0 and JNDI. I removed all the c3p0 configurations and it's working fine now.

Hibernate 建议使用第三方连接池,即 C3PO,并使用 JNDI 数据源来接收连接,但是,这似乎给该用户带来了问题;他们甚至谈论同时使用它们,好像这是一个明显的错误。

那么我不能同时使用它们吗,或者我应该按照 Hibernate 的建议使用它们吗?我要做的就是将 Hibernate 的默认连接池替换为用于生产环境的池,并将 Hibernate 配置为从在 JNDI 中注册的 javax.sql.Datasource 对象获取连接,正如他们所推荐的那样。

在参考 SO post 中,poster 从 Hibernate 配置的 C3PO 开始。当 poster 开始获取对 已配置 C3PO 连接池的 JNDI 引用时,he/she 认为 Hibernate 在启动期间正在处理该配置。解决方案是将 C3PO 配置移动到容器 (Tomcat) 并从 Hibernate 设置中删除配置。

下面是应用程序启动的简化概要:

  • 容器执行其启动操作
    • 创建连接并开始侦听连接
    • 创建和配置 JNDI 资源,如数据存储、邮件会话等
  • 部署网络应用程序
    • 扫描网络应用程序
    • 配置网络应用程序
    • 提供配置期间指定的任何 JNDI 资源
    • 完成部署

在 Web 应用程序部署阶段,会读取 Hibernate 配置并发出对 JNDI 资源的请求。由于 C3PO 已经配置,因此看不到在 Hibernate 中完成的任何配置。

我会尽力消除混乱。

我认为它始于非常简单的 DataSource 接口:https://docs.oracle.com/javase/7/docs/api/javax/sql/DataSource.html

The DataSource interface is implemented by a driver vendor. There are three types of implementations:

  1. Basic implementation -- produces a standard Connection object
  2. Connection pooling implementation -- produces a Connection object that will automatically participate in connection pooling. This implementation works with a middle-tier connection pooling manager.
  3. Distributed transaction implementation -- produces a Connection object that may be used for distributed transactions and almost always participates in connection pooling. This implementation works with a middle-tier transaction manager and almost always with a connection pooling manager.

Hibernate 需要使用数据源,并建议它使用连接池。

C3PO 包装现有的DataSource 并对其应用连接池,并创建一个类型2 的新DataSource。C3PO 假定它获取的DataSource 是类型1,但不能确定。

在其他应用程序服务器中,如果您声明在 JNDI 中注册的数据源,它几乎总是使用容器中已有的连接池。在Tomcat8的情况下,它内部使用了C3PO。

所以在Hibernate中实现连接池有两种方法:要么创建一个type 1数据源并将其嵌入代码中的连接池,要么在容器中声明你的数据源(带连接池),然后将其注入从 JNDI 休眠。

如果您同时执行这两种操作,就像您的情况一样,您应用程序中的 C3PO 从 JNDI 获取数据源,该数据源本身就是一个由 tomcat 管理的 C3PO 数据源。当应用程序尝试获取连接时,应用程序 C3PO 将调用容器 C3PO,这将创建实际的连接,但连接将被放入两个连接池中。当hibernate释放连接时,应用程序C3PO会保留它以供重用,但其他连接池也会一直等待连接被释放。

根据配置,底层连接池可能会在特定超时后终止连接。

所以在彼此之上配置两个连接池是危险的,完全没有必要。

回答赏金问题:在生产环境中,在生产容器中声明数据源并通过 JNDI 将其连接到 Hibernate,而无需在 Hibernate 中配置任何额外的连接池。