JDBC Batch inserts

Inserire nuovi record in una tabella è un'operazione semplicissima, ma se si ha la necessità di inserire centinaia di migliaia di record contemporaneamente (ad esempio si stanno importando molti dati da un file CSV) l'operazione può diventare molto -troppo- lenta. Analizziamo una possibile una soluzione, in Java, utilizzando le API JDBC.

Vogliamo una soluzione performante e al sicuro da SQL Injection, per cui utilizzeremo questo approccio:

  • apriamo la connessione verso il database
  • disabilitiamo l'autocommit
  • per ogni gruppo di N elementi da inserire (es 1.000)
  • chiudiamo la connessione
Una possibile implementazione di questo approccio è la seguente:

// l'oggetto ds è il datasource
DataSource ds = ... 
// Questi sono i dati da inserire sul database
// facciamo finta che siano in una lista
List<Oggetto> rows = ...
// inseriamo le righe 1000 per volta
final int batchSize = 1000;
// query di inserimento singolo record
String query = "INSERT INTO tabella(campo1, campo2) VALUES(?, ?)";
// Procediamo con le insert via batch/commit
try(Connection connection=ds.getConnection(); PreparedStatement ps = connection.prepareStatement(query)) {
	boolean previousAutocommit = connection.getAutoCommit();
	// l'autocommit viene disabilitato
	connection.setAutoCommit(false);
        int count = 0;
	for(Oggetto r: rows) {
		ps.setObject(1, r.getCampo1());
		ps.setObject(2, r.getCampo2());
		ps.addBatch();
		// al raggiungimento dei 1000 record facciamo il commit
		if(++count>=batchSize) {
			count = 0;
			ps.executeBatch();
			connection.commit();
		}
	}
	// commit dell'eventuale ultimo blocco 
	// con meno di 1000 record rimanente
	ps.executeBatch();
	connection.commit();
}	
finally {
	connection.setAutoCommit(previousAutocommit);
}

Nel caso in cui il database utilizzato sia MySQL, avrete un incremento delle perfomance notevole aggiungendo questi due parametri di connessione:

  • rewriteBatchedStatements=true
  • useCompression=true