Motivation
Ein JDBC-Connection-Pool ist essenziell für Anwendungsserver, auf denen mehrere parallele Anfragen Zugriff auf eine Datenbank benötigen. Besonders unter hoher Last ist ein effizienter JDBC-Connection-Pool wichtig, um blockierte Threads, verzögerte Anfrageverarbeitungen oder partielle Serviceausfälle zu vermeiden.
Payara, das eng mit der GlassFish-Referenzimplementierung von Jakarta EE (JEE) verwandt ist, bringt eine eigene Implementierung eines JDBC-Connection-Pools mit. Unter hoher Last traten bei unserer Anwendung jedoch Locking-Probleme auf. Glücklicherweise kann diese Implementierung mit Standardmitteln von Jakarta EE durch einen eigenen Connection-Pool ersetzt werden. HikariCP bietet eine schnelle, zuverlässige und schlanke Implementierung eines Connection-Pools ohne weitere Abhängigkeiten.
Data Source Definition
Jakarta EE stellt die Annotation DataSourceDefinition zur Verfügung, um eine Datenquelle zu deklarieren. Diese Annotation kann verwendet werden, um HikariCP in den Anwendungsserver einzubinden. Das folgende Beispiel der Klasse WrappedDataSource.java zeigt die Vorgehensweise:
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 unwrap(Class iface) throws SQLException {
return hikariDataSource.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class iface) throws SQLException {
return hikariDataSource.isWrapperFor(iface);
}
}
Dank der Klassen-Annotation erkennt der Anwendungsserver diese Klasse beim Starten und initialisiert HikariCP. Die Datenquelle kann somit in der JPA-Konfigurationsdatei persistence.xml über den deklarierten JNDI-Namen referenziert werden:
<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>
Dies führt dazu, dass HikariCP alle JDBC-Verbindungen bereitstellt, die von den @Transactional-Annotationen für JTA benötigt werden.
Alternativ ist ein direkter Zugriff auf die Datenquelle via Injection in CDI-Beans möglich:
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;
}
Diese Beispiele wurden auf Payara getestet. Da die DataSourceDefinition -Annotation Teil von Jakarta EE ist, sollte der gezeigte Beispielcode auf jedem Anwendungsserver anwendbar sein, der Jakarta EE implementiert.
Ein Beitrag von
Sascha Böhme
ist Software Architect bei der QAware. Er verantwortet als technischer Chefdesigner den Erfolg beim Kunden BMW. Sascha Böhme hat Informatik an der Technischen Universität Dresden studiert und [...]