I will try to clear the confusion.
I think this starts with a surprisingly simple DataSource interface: https://docs.oracle.com/javase/7/docs/api/javax/sql/DataSource.html
The DataSource interface is implemented by the driver provider. There are three types of implementation:
- The main implementation - creates a standard Connection object
- Implementation of the connection pool - creates a Connection object that will automatically participate in the connection pool. This implementation works with the middleware connection pool manager.
- Distributed transaction - creates a Connection object that can be used for distributed transactions and almost always participates in the connection pool. This implementation works with a mid-level transaction manager and almost always with a pool manager connection.
Hibernation requires a data source for operation and recommends using a connection pool.
C3PO wraps the existing DataSource and applies a connection pool to it and creates a new DataSource, which is type 2. C3PO assumes that the DataSource that it receives is type 1, but it cannot be sure.
In other application servers, if you declare a data source that is registered in JNDI, it almost always uses the connection pool already in the container. In the case of Tomcat 8, it uses C3PO internally.
Thus, there are two ways to reach the connection pool in Hibernate: either create a type 1 data source, or insert it into the connection pool in the code, or declare your data source (with connection pool) in the container and put it into sleep mode from JNDI.
If you do both, as in your case, C3PO in your application gets the data source from JNDI, which itself is a C3PO DataSource managed by tomcat. When an application tries to get a connection, the C3PO application will call the C3PO container, which will create the actual connection, but the connection will be combined into both connection pools. When hibernate releases the connection, the C3PO application will support it for reuse, but the other connection pool will continue to wait until the connection is also released.
Depending on the configuration, the base connection pool may kill the connection after a certain timeout.
Thus, setting up two connection pools on top of each other is dangerous and completely unnecessary.
To answer the bonus question: in production environments, declare the data source in your production container and connect it to Hibernate via JNDI without any additional connection pool configured in Hibernate.