En guide til Apache Commons DbUtils

1. Oversigt

Apache Commons DbUtils er et lille bibliotek, der gør arbejdet med JDBC meget lettere.

I denne artikel implementerer vi eksempler for at fremvise dets funktioner og muligheder.

2. Opsætning

2.1. Maven afhængigheder

Først skal vi tilføje commons-dbutils og h2 afhængigheder til vores pom.xml:

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

Du kan finde den nyeste version af commons-dbutils og h2 på Maven Central.

2.2. Test database

Med vores afhængigheder på plads, lad os oprette et script til at oprette de tabeller og poster, vi bruger:

OPRET TABEL medarbejder (id int IKKE NULL PRIMÆR NØGLE auto_increment, fornavn varchar (255), efternavn varchar (255), løn dobbelt, ansættelsesdato,); Opret TABEL-e-mail (id int IKKE NULL PRIMÆR NØGLE auto_increment, medarbejderid int, adresse varchar (255)); INDSÆT I medarbejder (fornavn, efternavn, løn, ansat dato) VÆRDIER ('John', 'Doe', 10000.10, to_date ('01 -01-2001 ',' dd-mm-åååå ')); // ... INDSÆT I E-mail (medarbejder-id, adresse) VÆRDIER (1, '[email protected]'); // ...

Alle eksempler på testsager i denne artikel bruger en nyoprettet forbindelse til en H2-hukommelsesdatabase:

offentlig klasse DbUtilsUnitTest {privat forbindelsesforbindelse; @Før offentligt ugyldigt setupDB () kaster undtagelse {Class.forName ("org.h2.Driver"); Streng db = "jdbc: h2: mem:; INIT = runskript fra 'classpath: /employees.sql'"; forbindelse = DriverManager.getConnection (db); } @ Efter offentlig ugyldighed closeBD () {DbUtils.closeQuietly (forbindelse); } // ...}

2.3. POJO'er

Endelig har vi brug for to enkle klasser:

offentlig klassemedarbejder {privat heltal id; privat streng fornavn; privat streng efternavn; privat dobbelt løn; privat dato ansat dato; // standardkonstruktører, getters og settere} public class Email {private Integer id; privat heltal medarbejder-id; privat strengadresse; // standardkonstruktører, getters og settere}

3. Introduktion

DbUtils-biblioteket tilbyder det QueryRunner klasse som hovedindgangssted for det meste af den tilgængelige funktionalitet.

Denne klasse fungerer ved at modtage en forbindelse til databasen, en SQL-sætning, der skal udføres, og en valgfri liste over parametre, der leverer værdier til forespørgslens pladsholdere.

Som vi vil se senere, modtager et par metoder også en ResultSetHandler implementering - som er ansvarlig for transformation ResultSet forekomster i de objekter, vores applikation forventer.

Naturligvis leverer biblioteket allerede flere implementeringer, der håndterer de mest almindelige transformationer, såsom lister, kort og JavaBeans.

4. Forespørgsel om data

Nu hvor vi kender det grundlæggende, er vi klar til at forespørge vores database.

Lad os starte med et hurtigt eksempel på at få alle poster i databasen som en liste over kort ved hjælp af en MapListHandler:

@Test offentlig ugyldighed givenResultHandler_whenExecutingQuery_thenExpectedList () kaster SQLException {MapListHandler beanListHandler = ny MapListHandler (); QueryRunner runner = ny QueryRunner (); Liste liste = runner.query (forbindelse, "VÆLG * FRA medarbejder", beanListHandler); assertEquals (list.size (), 5); assertEquals (list.get (0) .get ("fornavn"), "John"); assertEquals (list.get (4) .get ("firstname"), "Christian"); }

Herefter er her et eksempel på brug af en BeanListHandler at omdanne resultaterne til Medarbejder tilfælde:

@Test offentlig ugyldighed givenResultHandler_whenExecutingQuery_thenEmployeeList () kaster SQLException {BeanListHandler beanListHandler = new BeanListHandler (Employee.class); QueryRunner runner = ny QueryRunner (); Liste medarbejderliste = runner.query (forbindelse, "VÆLG * FRA medarbejder", beanListHandler); assertEquals (medarbejderliste.størrelse (), 5); assertEquals (medarbejderliste.get (0) .getFirstnavn (), "John"); assertEquals (medarbejderliste.get (4) .getFirstnavn (), "kristen"); }

For forespørgsler, der returnerer en enkelt værdi, kan vi bruge en ScalarHandler:

@Test offentlig ugyldighed givenResultHandler_whenExecutingQuery_thenExpectedScalar () kaster SQLException {ScalarHandler scalarHandler = ny ScalarHandler (); QueryRunner runner = ny QueryRunner (); Strengeforespørgsel = "VÆLG COUNT (*) FRA medarbejder"; lang optælling = runner.query (forbindelse, forespørgsel, scalarHandler); assertEquals (antal, 5); }

At lære alt det ResultatSerHandler implementeringer, kan du henvise til ResultSetHandler dokumentation.

4.1. Custom Handlers

Vi kan også oprette en brugerdefineret handler til at videregive til QueryRunner'S metoder, når vi har brug for mere kontrol over, hvordan resultaterne vil blive omdannet til objekter.

Dette kan gøres ved enten at implementere ResultSetHandler interface eller udvidelse af en af ​​de eksisterende implementeringer, der leveres af biblioteket.

Lad os se, hvordan den anden tilgang ser ud. Lad os først tilføje et andet felt til vores Medarbejder klasse:

offentlig klasse medarbejder {privat liste e-mails; // ...}

Lad os nu oprette en klasse, der udvider BeanListHandler skriv og indstiller e-mail-listen for hver medarbejder:

offentlig klasse EmployeeHandler udvider BeanListHandler {privat forbindelsesforbindelse; offentlig EmployeeHandler (Connection con) {super (Employee.class); this.connection = con; } @Override offentlig listehåndtag (ResultSet rs) kaster SQLException {List medarbejdere = super.handle (rs); QueryRunner runner = ny QueryRunner (); BeanListHandler-handler = ny BeanListHandler (Email.class); Strengeforespørgsel = "VÆLG * FRA e-mail HVOR medarbejderid =?"; for (Medarbejdermedarbejder: medarbejdere) {List emails = runner.query (forbindelse, forespørgsel, handler, medarbejder.getId ()); medarbejder.setE-mails (e-mails); } returnere medarbejdere }}

Bemærk, at vi forventer en Forbindelse objekt i konstruktøren, så vi kan udføre forespørgslerne for at få e-mails.

Lad os endelig teste vores kode for at se, om alt fungerer som forventet:

@Test offentlig ugyldighed givenResultHandler_whenExecutingQuery_thenEmailsSetted () kaster SQLException {EmployeeHandler medarbejderHandler = ny MedarbejderHandler (forbindelse); QueryRunner runner = ny QueryRunner (); Liste medarbejdere = runner.query (forbindelse, "VÆLG * FRA medarbejder", medarbejderHandler); assertEquals (medarbejdere.get (0) .getE-mails (). størrelse (), 2); assertEquals (medarbejdere.get (2) .getE-mails (). størrelse (), 3); }

4.2. Brugerdefinerede rækkeprocessorer

I vores eksempler er kolonnenavnene på medarbejder tabel matcher feltnavnene på vores Medarbejder klasse (matchningen er ikke bogstavfølsom). Det er dog ikke altid tilfældet - for eksempel når kolonnenavne bruger understregning til at adskille sammensatte ord.

I disse situationer kan vi drage fordel af RowProcessor interface og dets implementeringer for at kortlægge kolonnenavnene til de relevante felter i vores klasser.

Lad os se, hvordan det ser ud. Lad os først oprette en anden tabel og indsætte nogle poster i den:

OPRET TABEL medarbejderlegat (id int IKKE NULL PRIMÆR NØGLE auto_inkrement, fornavn varchar (255), efternavn varchar (255), dobbelt løn, ansat_dato dato,); INDSÆT I medarbejderlegat (fornavn, efternavn, løn, ansat dato) VÆRDIER ('John', 'Doe', 10000.10, til_dato ('01 -01-2001 ',' dd-mm-åååå ')); // ...

Lad os nu ændre vores MedarbejderHåndterer klasse:

public class EmployeeHandler udvider BeanListHandler {// ... public EmployeeHandler (Connection con) {super (Employee.class, ny BasicRowProcessor (ny BeanProcessor (getColumnToFieldsMap ()))); // ...} offentlig statisk kort getColumnsToFieldsMap () {Map columnsToFieldsMap = ny HashMap (); columnsToFieldsMap.put ("FIRST_NAME", "fornavn"); columnsToFieldsMap.put ("LAST_NAME", "efternavn"); columnsToFieldsMap.put ("HIRED_DATE", "hireDate"); returner kolonnerToFieldsMap; } // ...}

Bemærk, at vi bruger en BeanProcessor at foretage den faktiske kortlægning af kolonner til felter og kun til dem, der skal adresseres.

Lad os endelig teste, at alt er ok:

@Test offentlig ugyldighed givenResultHandler_whenExecutingQuery_thenAllPropertiesSetted () kaster SQLException {EmployeeHandler medarbejderHandler = ny MedarbejderHandler (forbindelse); QueryRunner runner = ny QueryRunner (); Strengeforespørgsel = "VÆLG * FRA medarbejderlegat"; Liste medarbejdere = runner.query (forbindelse, forespørgsel, medarbejderHandler); assertEquals ((int) medarbejdere.get (0) .getId (), 1); assertEquals (medarbejdere.get (0) .getFirstName (), "John"); }

5. Indsættelse af poster

Det QueryRunner klasse giver to tilgange til oprettelse af poster i en database.

Den første er at bruge opdater () metode og videregive SQL-sætningen og en valgfri liste over erstatningsparametre. Metoden returnerer antallet af indsatte poster:

@Test offentlig ugyldig nårInserting_thenInserted () kaster SQLException {QueryRunner runner = ny QueryRunner (); Streng insertSQL = "INDSÆT I medarbejder (fornavn, efternavn, løn, ansættelsesdato)" + "VÆRDIER (?,?,?,?)"; int numRowsInserted = runner.update (forbindelse, insertSQL, "Leia", "Kane", 60000.60, ny dato ()); assertEquals (numRowsInserted, 1); }

Den anden er at bruge indsæt () metode, der ud over SQL-sætningen og erstatningsparametrene har brug for en ResultSetHandler for at transformere de resulterende auto-genererede nøgler. Returneringsværdien er, hvad handler returnerer:

@Test offentlig ugyldighed givenHandler_whenInserting_thenExpectedId () kaster SQLException {ScalarHandler scalarHandler = ny ScalarHandler (); QueryRunner runner = ny QueryRunner (); String insertSQL = "INDSÆT I medarbejder (fornavn, efternavn, løn, ansættelsesdato)" + "VÆRDIER (?,?,?,?)"; int newId = runner.insert (forbindelse, insertSQL, scalarHandler, "Jenny", "Medici", 60000.60, ny dato ()); assertEquals (newId, 6); }

6. Opdatering og sletning

Det opdater () metode til QueryRunner klasse kan også bruges til at ændre og slette poster fra vores database.

Dens anvendelse er triviel. Her er et eksempel på, hvordan man opdaterer en medarbejders løn:

@Test offentlig ugyldighed givenSalary_whenUpdating_thenUpdated () kaster SQLException {dobbelt løn = 35000; QueryRunner runner = ny QueryRunner (); String updateSQL = "OPDATER medarbejder SÆT løn = løn * 1.1 HVOR løn <=?"; int numRowsUpdated = runner.update (forbindelse, updateSQL, løn); assertEquals (numRowsUpdated, 3); }

Og her er en anden for at slette en medarbejder med det givne id:

@Test offentlig ugyldig nårDeletingRecord_thenDeleted () kaster SQLException {QueryRunner runner = ny QueryRunner (); Streng deleteSQL = "SLET FRA medarbejder HVOR id =?"; int numRowsDeleted = runner.update (forbindelse, deleteSQL, 3); assertEquals (numRowsDeleted, 1); }

7. Asynkrone operationer

DbUtils leverer AsyncQueryRunner klasse til at udføre operationer asynkront. Metoderne på denne klasse har en korrespondance med metoderne fra QueryRunner klasse, bortset fra at de returnerer a Fremtid eksempel.

Her er et eksempel på at få alle medarbejdere i databasen og vente i op til 10 sekunder for at få resultaterne:

@Test offentlig ugyldighed givenAsyncRunner_whenExecutingQuery_thenExpectedList () kaster undtagelse {AsyncQueryRunner runner = ny AsyncQueryRunner (Executors.newCachedThreadPool ()); EmployeeHandler medarbejderHandler = ny MedarbejderHandler (forbindelse); Strengeforespørgsel = "VÆLG * FRA medarbejder"; Fremtid fremtid = runner.query (forbindelse, forespørgsel, medarbejderHandler); Liste medarbejderliste = fremtid.get (10, TimeUnit.SECONDS); assertEquals (medarbejderliste.størrelse (), 5); }

8. Konklusion

I denne vejledning udforskede vi de mest bemærkelsesværdige funktioner i Apache Commons DbUtils-biblioteket.

Vi forespurgte data og transformerede dem til forskellige objekttyper, indsatte poster, der opnåede de genererede primære nøgler, og opdaterede og slettede data baseret på et givet kriterium. Vi benyttede os også af AsyncQueryRunner klasse for at asynkront udføre en forespørgsel.

Og som altid kan den komplette kildekode til denne artikel findes på Github.