Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q
blog-icon Blog

2 Min. Lesezeit

HikariCP unter Jakarta EE

HikariCP unter Jakarta EE

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 [...]