Motivation
A JDBC connection pool is essential for application servers where several parallel requests need access to a database. Especially on high load, an efficient JDBC connection pool is important to avoid locked threads, delayed request processing or partial service interruptions.
Payara, closely related to the GlassFish reference implementation of Jakarta EE (JEE), comes with its own implementation of a JDBC connection pool. Under high load our application experienced locking deficiencies when it comes to high load. Fortunately, this implementation can be replaced with a custom connection pool using standard means of Jakarta EE. HikariCP offers a fast, reliable and small implementation of a connection pool without further dependencies.
Data source definition
Jakarta EE provides the annotation DataSourceDefinition
to declare a data source. This annotation can be used to inject HikariCP into the application server. The following example class WrappedDataSource.java
shows how to proceed:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
| package full.pkg.name;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.postgresql.ds.PGSimpleDataSource;
import javax.annotation.sql.DataSourceDefinition;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
@DataSourceDefinition(
name = "java:app/jdbc/DefaultDb", // the JNDI name referenced in the persistence.xml
className = "full.pkg.name.WrappedDataSource"
)
public class WrappedDataSource implements DataSource {
private final HikariDataSource hikariDataSource;
/**
* Creates a new {@link WrappedDataSource}.
*/
public WrappedDataSource() {
// set-up the underlying JDBC datasource for accessing the database (PostgreSQL, Oracle)
PGSimpleDataSource ds = ...;
HikariConfig hc = new HikariConfig();
hc.setDataSource(ds);
// configure HikariCP as needed
hc.setMinimumIdle(...);
hc.setMaximumPoolSize(...);
hc.setConnectionTimeout(...);
hc.setIdleTimeout(...);
hc.setRegisterMbeans(...);
hikariDataSource = new HikariDataSource(hc);
}
@Override
public Connection getConnection() throws SQLException {
return hikariDataSource.getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return hikariDataSource.getConnection(username, password);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return hikariDataSource.getLogWriter();
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
hikariDataSource.setLogWriter(out);
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
hikariDataSource.setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException {
return hikariDataSource.getLoginTimeout();
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return hikariDataSource.getParentLogger();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return hikariDataSource.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return hikariDataSource.isWrapperFor(iface);
}
}
|
Thanks to the class annotation, the application server discovers this class on start-up and initializes HikariCP. The data source can thus be referenced in the JPA configuration file persistence.xml
by the declared JNDI name:
1
2
3
4
5
6
7
8
9
10
| <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="Default" transaction-type="JTA">
<jta-data-source>java:app/jdbc/DefaultDb</jta-data-source>
<!-- further configuration -->
</persistence-unit>
</persistence>
|
This results in HikariCP providing all JDBC connections required by @Transactional
annotations for JTA.
Alternatively, direct access to the data source is possible via injection into CDI beans:
1
2
3
4
5
6
7
8
9
10
11
| import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.sql.DataSource;
@ApplicationScoped
public class SomeClass {
@Resource(lookup = "java:app/jdbc/DefaultDb")
private DataSource dataSource;
}
|
These examples have been tested on Payara. Since the DataSourceDefinition
annotation is part of Jakarta EE, the shown example code should be applicable to any application server which implements Jakarta EE.