Introduzione a HikariCP

1. Panoramica

In questo articolo introduttivo, impareremo a conoscere il progetto del pool di connessioni JDBC HikariCP. Si tratta di un framework di pool di connessioni JDBC molto leggero (a circa 130 KB) e velocissimo sviluppato da Brett Wooldridge intorno al 2012.

2. Introduzione

Sono disponibili diversi risultati di benchmark per confrontare le prestazioni di HikariCP con altri framework di pool di connessioni come c3p0 , dbcp2 , tomcat e vibur . Ad esempio, il team HikariCP ha pubblicato sotto i benchmark (risultati originali disponibili qui):

Il framework è così veloce perché sono state applicate le seguenti tecniche:

  • Progettazione a livello di bytecode: è stata eseguita un'ingegneria a livello di bytecode estremo (inclusa la codifica nativa a livello di assembly)
  • Micro-ottimizzazioni: sebbene appena misurabili, queste ottimizzazioni combinate aumentano le prestazioni complessive
  • L'uso intelligente del quadro collezioni - l'ArrayList è stato sostituito con una classe personalizzata elenco rapido che elimina Gamma controllo e la rimozione esegue scansioni da coda alla testa

3. Dipendenza da Maven

Creiamo un'applicazione di esempio per evidenziarne l'utilizzo. HikariCP viene fornito con il supporto per tutte le versioni principali di JVM. Ogni versione richiede la sua dipendenza; per Java da 8 a 11, abbiamo:

 com.zaxxer HikariCP 3.4.5 

Sono supportate anche le versioni precedenti di JDK come la 6 e la 7. Le versioni appropriate possono essere trovate qui e qui. Inoltre, possiamo controllare le ultime versioni nel Central Maven Repository.

4. Utilizzo

Creiamo ora un'applicazione demo. Si noti che è necessario includere una dipendenza della classe del driver JDBC adatta nel pom.xml . Se non vengono fornite dipendenze, l'applicazione genererà un'eccezione ClassNotFoundException .

4.1. Creazione di un DataSource

Useremo DataSource di HikariCP per creare una singola istanza di un'origine dati per la nostra applicazione:

public class DataSource { private static HikariConfig config = new HikariConfig(); private static HikariDataSource ds; static { config.setJdbcUrl( "jdbc_url" ); config.setUsername( "database_username" ); config.setPassword( "database_password" ); config.addDataSourceProperty( "cachePrepStmts" , "true" ); config.addDataSourceProperty( "prepStmtCacheSize" , "250" ); config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" ); ds = new HikariDataSource( config ); } private DataSource() {} public static Connection getConnection() throws SQLException { return ds.getConnection(); } }

Punto da notare qui è l'inizializzazione nel blocco statico .

HikariConfig è la classe di configurazione utilizzata per inizializzare un'origine dati. Viene fornito con quattro noti parametri da utilizzare come nome utente , password , jdbcUrl , dataSourceClassName .

Fuori jdbcUrl e dataSourceClassName , deve essere utilizzato uno alla volta. Tuttavia, quando si utilizza questa proprietà con driver meno recenti, potrebbe essere necessario impostare entrambe le proprietà.

Oltre a queste proprietà, sono disponibili molte altre proprietà che potrebbero non essere tutte offerte da altri framework di pool:

  • autoCommit
  • connesione finita
  • idleTimeout
  • maxLifetime
  • connectionTestQuery
  • connectionInitSql
  • validationTimeout
  • maximumPoolSize
  • poolName
  • allowPoolSuspension
  • sola lettura
  • transactionIsolation
  • leakDetectionThreshold

HikariCP si distingue per queste proprietà del database. È abbastanza avanzato da rilevare anche da solo le perdite di connessione!

Una descrizione dettagliata di queste proprietà può essere trovata qui.

Possiamo anche inizializzare HikariConfig con un file delle proprietà posizionato nella directory delle risorse :

private static HikariConfig config = new HikariConfig( "datasource.properties" );

Il file delle proprietà dovrebbe essere simile a questo:

dataSourceClassName= //TBD dataSource.user= //TBD //other properties name should start with dataSource as shown above

Possiamo usare anche la configurazione basata su java.util.Properties :

Properties props = new Properties(); props.setProperty( "dataSourceClassName" , //TBD ); props.setProperty( "dataSource.user" , //TBD ); //setter for other required properties private static HikariConfig config = new HikariConfig( props );

In alternativa, possiamo inizializzare direttamente un'origine dati:

ds.setJdbcUrl( //TBD ); ds.setUsername( //TBD ); ds.setPassword( //TBD );

4.2. Utilizzo di un'origine dati

Ora che abbiamo definito l'origine dati, possiamo utilizzarla per ottenere una connessione dal pool di connessioni configurato ed eseguire azioni correlate a JDBC.

Supponiamo di avere due tabelle denominate dept ed emp per simulare un caso d'uso del reparto dipendente. Scriveremo una classe per recuperare quei dettagli dal database usando HikariCP.

Di seguito elenchiamo le istruzioni SQL necessarie per creare i dati di esempio:

create table dept( deptno numeric, dname varchar(14), loc varchar(13), constraint pk_dept primary key ( deptno ) ); create table emp( empno numeric, ename varchar(10), job varchar(9), mgr numeric, hiredate date, sal numeric, comm numeric, deptno numeric, constraint pk_emp primary key ( empno ), constraint fk_deptno foreign key ( deptno ) references dept ( deptno ) ); insert into dept values( 10, 'ACCOUNTING', 'NEW YORK' ); insert into dept values( 20, 'RESEARCH', 'DALLAS' ); insert into dept values( 30, 'SALES', 'CHICAGO' ); insert into dept values( 40, 'OPERATIONS', 'BOSTON' ); insert into emp values( 7839, 'KING', 'PRESIDENT', null, to_date( '17-11-1981' , 'dd-mm-yyyy' ), 7698, null, 10 ); insert into emp values( 7698, 'BLAKE', 'MANAGER', 7839, to_date( '1-5-1981' , 'dd-mm-yyyy' ), 7782, null, 20 ); insert into emp values( 7782, 'CLARK', 'MANAGER', 7839, to_date( '9-6-1981' , 'dd-mm-yyyy' ), 7566, null, 30 ); insert into emp values( 7566, 'JONES', 'MANAGER', 7839, to_date( '2-4-1981' , 'dd-mm-yyyy' ), 7839, null, 40 );

Nota se usiamo un database in memoria come H2, dobbiamo caricare automaticamente lo script del database prima di eseguire il codice effettivo per recuperare i dati. Per fortuna, H2 viene fornito con un parametro INIT che può caricare lo script del database dal classpath in fase di esecuzione. L'URL JDBC dovrebbe essere simile a:

jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;INIT=runscript from 'classpath:/db.sql'

Dobbiamo creare un metodo per recuperare questi dati dal database:

public static List fetchData() throws SQLException { String SQL_QUERY = "select * from emp"; List employees = null; try (Connection con = DataSource.getConnection(); PreparedStatement pst = con.prepareStatement( SQL_QUERY ); ResultSet rs = pst.executeQuery();) { employees = new ArrayList(); Employee employee; while ( rs.next() ) { employee = new Employee(); employee.setEmpNo( rs.getInt( "empno" ) ); employee.setEname( rs.getString( "ename" ) ); employee.setJob( rs.getString( "job" ) ); employee.setMgr( rs.getInt( "mgr" ) ); employee.setHiredate( rs.getDate( "hiredate" ) ); employee.setSal( rs.getInt( "sal" ) ); employee.setComm( rs.getInt( "comm" ) ); employee.setDeptno( rs.getInt( "deptno" ) ); employees.add( employee ); } } return employees; }

Ora, dobbiamo creare un metodo JUnit per testarlo. Poiché conosciamo il numero di righe nella tabella emp , possiamo aspettarci che la dimensione dell'elenco restituito sia uguale al numero di righe:

@Test public void givenConnection_thenFetchDbData() throws SQLException { HikariCPDemo.fetchData(); assertEquals( 4, employees.size() ); }

5. conclusione

In questo rapido tutorial, abbiamo appreso i vantaggi dell'utilizzo di HikariCP e la sua configurazione.

Come sempre, il codice sorgente completo è disponibile su GitHub.