Een gids voor Apache Commons DbUtils

1. Overzicht

Apache Commons DbUtils is een kleine bibliotheek die het werken met JDBC een stuk eenvoudiger maakt.

In dit artikel zullen we voorbeelden implementeren om de functies en mogelijkheden ervan te laten zien.

2. Installatie

2.1. Afhankelijkheden van Maven

Eerst moeten we de commons-dbutils en h2 afhankelijkheden van onze pom.xml:

 commons-dbutils commons-dbutils 1.6 com.h2database h2 1.4.196 

Je kunt de nieuwste versie van commons-dbutils en h2 vinden op Maven Central.

2.2. Testdatabase

Laten we met onze afhankelijkheden een script maken om de tabellen en records te maken die we zullen gebruiken:

CREATE TABLE werknemer (id int NOT NULL PRIMAIRE SLEUTEL auto_increment, voornaam varchar (255), achternaam varchar (255), salaris verdubbeld, datum ingehuurd,); CREATE TABLE email (id int NOT NULL PRIMARY KEY auto_increment, employeeid int, adres varchar (255)); INVOEGEN IN werknemer (voornaam, achternaam, salaris, ingehuurde datum) WAARDEN ('Jan', 'Doe', 10000.10, tot_datum ('01 -01-2001 ',' dd-mm-jjjj ')); // ... INSERT IN email (employeeid, address) VALUES (1, '[email protected]'); // ...

Alle voorbeeldtestgevallen in dit artikel gebruiken een nieuw gemaakte verbinding met een H2 in-memory-database:

openbare klasse DbUtilsUnitTest {privéverbindingsverbinding; @Before public void setupDB () gooit uitzondering {Class.forName ("org.h2.Driver"); String db = "jdbc: h2: mem:; INIT = runscript van 'classpath: /employees.sql'"; connection = DriverManager.getConnection (db); } @After public void closeBD () {DbUtils.closeQuietly (verbinding); } // ...}

2.3. POJO's

Ten slotte hebben we twee eenvoudige klassen nodig:

openbare klasse Medewerker {privé Geheel getal-id; private String voornaam; private String achternaam; particulier Dubbel salaris; privé Date hiredDate; // standard constructors, getters, and setters} public class Email {private Integer id; privé Integer employeeId; privé String-adres; // standaard constructeurs, getters en setters}

3. Inleiding

De DbUtils-bibliotheek biedt de QueryRunner class als het belangrijkste toegangspunt voor de meeste beschikbare functionaliteit.

Deze klasse werkt door een verbinding met de database te ontvangen, een SQL-instructie die moet worden uitgevoerd en een optionele lijst met parameters om waarden op te geven voor de tijdelijke aanduidingen van de query.

Zoals we later zullen zien, krijgen een paar methoden ook een ResultSetHandler implementatie - die verantwoordelijk is voor transformatie ResultSet instanties in de objecten die onze applicatie verwacht.

Natuurlijk biedt de bibliotheek al verschillende implementaties die de meest voorkomende transformaties verwerken, zoals lijsten, kaarten en JavaBeans.

4. Gegevens opvragen

Nu we de basis kennen, zijn we klaar om onze database te doorzoeken.

Laten we beginnen met een snel voorbeeld van het verkrijgen van alle records in de database als een lijst met kaarten met behulp van een MapListHandler:

@Test openbare leegte gegevenResultHandler_whenExecutingQuery_thenExpectedList () gooit SQLException {MapListHandler beanListHandler = nieuwe MapListHandler (); QueryRunner runner = nieuwe QueryRunner (); Lijst list = runner.query (verbinding, "SELECTEER * VAN werknemer", beanListHandler); assertEquals (list.size (), 5); assertEquals (list.get (0) .get ("firstname"), "John"); assertEquals (list.get (4) .get ("firstname"), "Christian"); }

Hier volgt een voorbeeld met een BeanListHandler om de resultaten om te zetten in Werknemer gevallen:

@Test openbare ongeldig gegevenResultHandler_whenExecutingQuery_thenEmployeeList () gooit SQLException {BeanListHandler beanListHandler = nieuwe BeanListHandler (Employee.class); QueryRunner runner = nieuwe QueryRunner (); List employeeList = runner.query (verbinding, "SELECT * FROM werknemer", beanListHandler); assertEquals (employeeList.size (), 5); assertEquals (employeeList.get (0) .getFirstName (), "John"); assertEquals (employeeList.get (4) .getFirstName (), "Christian"); }

Voor zoekopdrachten die een enkele waarde retourneren, kunnen we een ScalarHandler:

@Test openbare leegte gegevenResultHandler_whenExecutingQuery_thenExpectedScalar () gooit SQLException {ScalarHandler scalarHandler = nieuwe ScalarHandler (); QueryRunner runner = nieuwe QueryRunner (); String query = "SELECTEER AANTAL (*) VAN werknemer"; lange telling = runner.query (verbinding, vraag, scalarHandler); assertEquals (count, 5); }

Om alle ResultSerHandler implementaties, kunt u verwijzen naar het ResultSetHandler documentatie.

4.1. Aangepaste handlers

We kunnen ook een aangepaste handler maken om naar door te geven QueryRunner‘S methoden wanneer we meer controle nodig hebben over hoe de resultaten zullen worden omgezet in objecten.

Dit kan worden gedaan door ofwel het ResultSetHandler interface of uitbreiding van een van de bestaande implementaties van de bibliotheek.

Laten we eens kijken hoe de tweede benadering eruitziet. Laten we eerst nog een veld toevoegen aan onze Werknemer klasse:

openbare klasse Medewerker {privélijst e-mails; // ...}

Laten we nu een klasse maken die de extensie BeanListHandler typ en stelt de e-maillijst in voor elke werknemer:

openbare klasse EmployeeHandler breidt BeanListHandler {private Connection-verbinding uit; openbare EmployeeHandler (Connection con) {super (Employee.class); this.connection = con; } @Override public List handle (ResultSet rs) gooit SQLException {List workers = super.handle (rs); QueryRunner runner = nieuwe QueryRunner (); BeanListHandler-handler = nieuwe BeanListHandler (Email.class); String query = "SELECTEER * VAN E-mail WAAR employeeid =?"; voor (Werknemer werknemer: werknemers) {Lijst e-mails = runner.query (verbinding, vraag, handler, werknemer.getId ()); employee.setEmails (e-mails); } terugkerende werknemers; }}

Merk op dat we een Verbinding object in de constructor zodat we de queries kunnen uitvoeren om de e-mails op te halen.

Laten we tot slot onze code testen om te zien of alles werkt zoals verwacht:

@Test openbare ongeldig gegevenResultHandler_whenExecutingQuery_thenEmailsSetted () gooit SQLException {EmployeeHandler employeeHandler = nieuwe EmployeeHandler (verbinding); QueryRunner runner = nieuwe QueryRunner (); Lijst werknemers = runner.query (verbinding, "SELECTEER * VAN werknemer", employeeHandler); assertEquals (workers.get (0) .getEmails (). size (), 2); assertEquals (workers.get (2) .getEmails (). size (), 3); }

4.2. Aangepaste rijprocessors

In onze voorbeelden zijn de kolomnamen van de werknemer tabel komen overeen met de veldnamen van onze Werknemer class (de overeenkomst is niet hoofdlettergevoelig). Dat is echter niet altijd het geval, bijvoorbeeld wanneer kolomnamen onderstrepingstekens gebruiken om samengestelde woorden te scheiden.

In deze situaties kunnen we profiteren van de RijProcessor interface en zijn implementaties om de kolomnamen toe te wijzen aan de juiste velden in onze klassen.

Laten we eens kijken hoe dit eruit ziet. Laten we eerst nog een tabel maken en er enkele records in invoegen:

CREATE TABLE werknemer_legacy (id int NOT NULL PRIMAIRE SLEUTEL auto_increment, voornaam varchar (255), achternaam varchar (255), salaris dubbel, datum ingehuurd,); VOEG IN werknemer_legacy (voornaam, achternaam, salaris, ingehuurde_datum) WAARDEN ('Jan', 'Doe', 10000.10, tot_datum ('01 -01-2001 ',' dd-mm-jjjj ')); // ...

Laten we nu onze WerknemerHandler klasse:

openbare klasse EmployeeHandler breidt BeanListHandler uit {// ... openbare EmployeeHandler (Connection con) {super (Employee.class, nieuwe BasicRowProcessor (nieuwe BeanProcessor (getColumnsToFieldsMap ()))); // ...} openbare statische kaart getColumnsToFieldsMap () {Map columnsToFieldsMap = nieuwe HashMap (); columnsToFieldsMap.put ("FIRST_NAME", "firstName"); columnsToFieldsMap.put ("LAST_NAME", "achternaam"); columnsToFieldsMap.put ("HIRED_DATE", "hiredDate"); return columnsToFieldsMap; } // ...}

Merk op dat we een BeanProcessor om de daadwerkelijke toewijzing van kolommen aan velden te doen en alleen voor degenen die moeten worden geadresseerd.

Laten we tot slot testen of alles in orde is:

@Test openbare ongeldig gegevenResultHandler_whenExecutingQuery_thenAllPropertiesSetted () gooit SQLException {EmployeeHandler employeeHandler = nieuwe EmployeeHandler (verbinding); QueryRunner runner = nieuwe QueryRunner (); String query = "SELECTEER * VAN werknemer_legacy"; Lijst werknemers = runner.query (verbinding, vraag, employeeHandler); assertEquals ((int) medewerkers.get (0) .getId (), 1); assertEquals (workers.get (0) .getFirstName (), "John"); }

5. Records invoegen

De QueryRunner class biedt twee benaderingen voor het maken van records in een database.

De eerste is om de bijwerken() methode en geef de SQL-instructie en een optionele lijst met vervangende parameters door. De methode retourneert het aantal ingevoegde records:

@Test openbare leegte whenInserting_thenInserted () gooit SQLException {QueryRunner runner = nieuwe QueryRunner (); String insertSQL = "VOEG IN werknemer (voornaam, achternaam, salaris, ingehuurde datum)" + "WAARDEN (?,?,?,?)"; int numRowsInserted = runner.update (verbinding, insertSQL, "Leia", "Kane", 60000.60, nieuwe datum ()); assertEquals (numRowsInserted, 1); }

De tweede is om de invoegen () methode die, naast de SQL-instructie en vervangingsparameters, een ResultSetHandler om de resulterende automatisch gegenereerde sleutels te transformeren. De geretourneerde waarde is wat de handler retourneert:

@Test openbare leegte gegevenHandler_whenInserting_thenExpectedId () gooit SQLException {ScalarHandler scalarHandler = nieuwe ScalarHandler (); QueryRunner runner = nieuwe QueryRunner (); String insertSQL = "VOEG IN werknemer (voornaam, achternaam, salaris, ingehuurde datum)" + "WAARDEN (?,?,?,?)"; int newId = runner.insert (verbinding, insertSQL, scalarHandler, "Jenny", "Medici", 60000.60, nieuwe datum ()); assertEquals (newId, 6); }

6. Bijwerken en verwijderen

De bijwerken() methode van de QueryRunner class kan ook worden gebruikt om records uit onze database te wijzigen en te wissen.

Het gebruik ervan is triviaal. Hier is een voorbeeld van hoe u het salaris van een werknemer kunt bijwerken:

@Test openbare leegte gegevenSalary_whenUpdating_thenUpdated () gooit SQLException {dubbel salaris = 35000; QueryRunner runner = nieuwe QueryRunner (); String updateSQL = "UPDATE werknemer SET salaris = salaris * 1.1 WAAR salaris <=?"; int numRowsUpdated = runner.update (verbinding, updateSQL, salaris); assertEquals (numRowsUpdated, 3); }

En hier is er nog een om een ​​werknemer met de opgegeven ID te verwijderen:

@Test openbare leegte whenDeletingRecord_thenDeleted () gooit SQLException {QueryRunner runner = nieuwe QueryRunner (); String deleteSQL = "VERWIJDEREN VAN werknemer WAAR id =?"; int numRowsDeleted = runner.update (verbinding, deleteSQL, 3); assertEquals (numRowsDeleted, 1); }

7. Asynchrone bewerkingen

DbUtils biedt de AsyncQueryRunner class om bewerkingen asynchroon uit te voeren. De methoden op deze klasse komen overeen met die van QueryRunner klasse, behalve dat ze een Toekomst voorbeeld.

Hier is een voorbeeld om alle werknemers in de database te krijgen, wachtend tot 10 seconden om de resultaten te krijgen:

@ Test openbare ongeldige gegevenAsyncRunner_whenExecutingQuery_thenExpectedList () gooit uitzondering {AsyncQueryRunner runner = nieuwe AsyncQueryRunner (Executors.newCachedThreadPool ()); EmployeeHandler employeeHandler = nieuwe EmployeeHandler (verbinding); String query = "SELECTEER * VAN werknemer"; Toekomst future = runner.query (verbinding, vraag, employeeHandler); Lijst employeeList = future.get (10, TimeUnit.SECONDS); assertEquals (employeeList.size (), 5); }

8. Conclusie

In deze tutorial hebben we de meest opvallende kenmerken van de Apache Commons DbUtils-bibliotheek onderzocht.

We hebben gegevens opgevraagd en omgezet in verschillende objecttypen, records ingevoegd om de gegenereerde primaire sleutels te verkrijgen en gegevens bijgewerkt en verwijderd op basis van een bepaald criterium. We hebben ook geprofiteerd van de AsyncQueryRunner class om een ​​querybewerking asynchroon uit te voeren.

En, zoals altijd, is de volledige broncode voor dit artikel te vinden op Github.