Een gids voor Jdbi

1. Inleiding

In dit artikel gaan we bekijken hoe je een relationele database kunt opvragen met jdbi.

Jdbi is een open source Java-bibliotheek (Apache-licentie) die lambda-expressies en reflectie gebruikt om een ​​vriendelijkere interface van hoger niveau te bieden dan JDBC om toegang te krijgen tot de database.

Jdbi is echter geen ORM; ook al heeft het een optionele module voor het toewijzen van SQL-objecten, het heeft geen sessie met gekoppelde objecten, een database-onafhankelijkheidslaag en andere toeters en bellen van een typische ORM.

2. Jdbi-instellingen

Jdbi is georganiseerd in een kern en verschillende optionele modules.

Om te beginnen, hoeven we alleen de kernmodule in onze afhankelijkheden op te nemen:

  org.jdbi jdbi3-core 3.1.0 

In de loop van dit artikel laten we voorbeelden zien met behulp van de HSQL-database:

 org.hsqldb hsqldb 2.4.0 test 

We kunnen de laatste versie van jdbi3-core, HSQLDB en de andere Jdbi-modules op Maven Central.

3. Verbinding maken met de database

Eerst moeten we verbinding maken met de database. Om dat te doen, moeten we de verbindingsparameters specificeren.

Het uitgangspunt is het Jdbi klasse:

Jdbi jdbi = Jdbi.create ("jdbc: hsqldb: mem: testDB", "sa", "");

Hier specificeren we de verbindings-URL, een gebruikersnaam en natuurlijk een wachtwoord.

3.1. Aanvullende parameters

Als we andere parameters moeten opgeven, gebruiken we een overbelaste methode die een Eigendommen voorwerp:

Eigenschappen properties = nieuwe Eigenschappen (); properties.setProperty ("gebruikersnaam", "sa"); properties.setProperty ("wachtwoord", ""); Jdbi jdbi = Jdbi.create ("jdbc: hsqldb: mem: testDB", eigenschappen);

In deze voorbeelden hebben we de Jdbi instantie in een lokale variabele. Dat komt omdat we het zullen gebruiken om verklaringen en vragen naar de database te sturen.

In feite alleen maar bellen creëren maakt geen verbinding met de database. Het slaat gewoon de verbindingsparameters op voor later.

3.2. Gebruik maken van een Databron

Als we verbinding maken met de database met behulp van een Databron, zoals gewoonlijk het geval is, kunnen we de juiste gebruiken creëren overbelasten:

Jdbi jdbi = Jdbi.create (gegevensbron);

3.3. Werken met handvatten

Werkelijke verbindingen met de database worden weergegeven door instanties van de Handvat klasse.

De gemakkelijkste manier om met handvatten te werken en ze automatisch te laten sluiten, is door lambda-uitdrukkingen te gebruiken:

jdbi.useHandle (handle -> {doStuffWith (handle);});

Wij bellen useHandle als we geen waarde hoeven terug te geven.

Anders gebruiken we metHandle:

jdbi.withHandle (handle -> {return computeValue (handle);});

Het is ook mogelijk, maar niet aanbevolen, om handmatig een verbindingshandle te openen; in dat geval moeten we het sluiten als we klaar zijn:

Jdbi jdbi = Jdbi.create ("jdbc: hsqldb: mem: testDB", "sa", ""); probeer (Handle handle = jdbi.open ()) {doStuffWith (handle); }

Gelukkig, zoals we kunnen zien, Handvat werktuigen Afsluitbaar, dus het kan worden gebruikt met try-with-resources.

4. Eenvoudige verklaringen

Nu we weten hoe we een verbinding kunnen krijgen, gaan we kijken hoe we deze kunnen gebruiken.

In dit gedeelte maken we een eenvoudige tabel die we in het hele artikel zullen gebruiken.

Om verklaringen te verzenden zoals maak een tafel naar de database gebruiken we de uitvoeren methode:

handle.execute ("maak tabelproject" + "(id geheel getal identiteit, naam varchar (50), url varchar (100))");

uitvoeren geeft het aantal rijen terug dat werd beïnvloed door de instructie:

int updateCount = handle.execute ("invoegen in projectwaarden" + "(1, 'tutorials', 'github.com/eugenp/tutorials')"); assertEquals (1, updateCount);

Eigenlijk is uitvoeren slechts een gemakkelijke methode.

We zullen in latere secties naar meer complexe use-cases kijken, maar voordat we dat doen, moeten we leren hoe we resultaten uit de database kunnen extraheren.

5. Opvragen van de database

De meest eenvoudige uitdrukking die resultaten van de database oplevert, is een SQL-query.

Om een ​​query uit te voeren met een Jdbi-handle, moeten we ten minste:

  1. maak de query
  2. kies hoe u elke rij wilt weergeven
  3. herhaal de resultaten

We zullen nu elk van de bovenstaande punten bekijken.

5.1. Een zoekopdracht maken

Niet verrassend, Jdbi vertegenwoordigt query's als instanties van het Vraag klasse.

We kunnen er een krijgen van een handvat:

Queryquery = handle.createQuery ("selecteer * uit project");

5.2. De resultaten in kaart brengen

Jdbi abstraheert weg van de JDBC ResultSet, die een nogal omslachtige API heeft.

Daarom biedt het verschillende mogelijkheden om toegang te krijgen tot de kolommen die het resultaat zijn van een query of een andere instructie die een resultaat retourneert. We zullen nu de eenvoudigste zien.

We kunnen elke rij als een kaart weergeven:

query.mapToMap ();

De sleutels van de kaart zijn de geselecteerde kolomnamen.

Of, wanneer een query een enkele kolom retourneert, kunnen we deze toewijzen aan het gewenste Java-type:

handle.createQuery ("selecteer naam uit project"). mapTo (String.class);

Jdbi heeft ingebouwde mappers voor veel gangbare klassen. Degenen die specifiek zijn voor een bibliotheek- of databasesysteem, worden geleverd in afzonderlijke modules.

Natuurlijk kunnen we ook onze mappers definiëren en registreren. We zullen er later over praten.

Ten slotte kunnen we rijen toewijzen aan een bean of een andere aangepaste klasse. Nogmaals, we zullen de meer geavanceerde opties in een speciale sectie zien.

5.3. De resultaten herhalen

Zodra we hebben besloten hoe we de resultaten in kaart brengen door de juiste methode aan te roepen, we ontvangen een Resultaat: wijzigbaar voorwerp.

We kunnen het vervolgens gebruiken om de resultaten te herhalen, rij voor rij.

Hier zullen we de meest voorkomende opties bekijken.

We kunnen de resultaten alleen in een lijst samenvoegen:

Lijst resultaten = query.mapToMap (). lijst ();

Of naar een ander Verzameling type:

Lijstresultaten = query.mapTo (String.class) .collect (Collectors.toSet ());

Of we kunnen de resultaten herhalen als een stream:

query.mapTo (String.class) .useStream ((Stream stream) -> {doStuffWith (stream)});

Hier hebben we expliciet de stroom variabel voor de duidelijkheid, maar het is niet nodig.

5.4. Een enkel resultaat krijgen

Als een speciaal geval, wanneer we slechts één rij verwachten of daarin geïnteresseerd zijn, hebben we een aantal speciale methoden beschikbaar.

Als we willen hoogstens één resultaat, we kunnen gebruiken findFirst:

Optioneel first = query.mapToMap (). findFirst ();

Zoals we kunnen zien, retourneert het een Optioneel waarde, die alleen aanwezig is als de query ten minste één resultaat retourneert.

Als de query meer dan één rij retourneert, wordt alleen de eerste geretourneerd.

Als we in plaats daarvan willen één en slechts één resultaat, we gebruiken alleen vinden:

Date onlyResult = query.mapTo (Date.class) .findOnly ();

Ten slotte, als er nul of meer dan één resultaten zijn, alleen vinden gooit een IllegalStateException.

6. Bindende parameters

Vaak, queries hebben een vast gedeelte en een geparametriseerd gedeelte. Dit heeft verschillende voordelen, waaronder:

  • beveiliging: door stringconcatenatie te vermijden, voorkomen we SQL-injectie
  • gemak: we hoeven de exacte syntaxis van complexe gegevenstypen zoals tijdstempels niet te onthouden
  • prestaties: het statische gedeelte van de query kan één keer worden geparseerd en in de cache worden opgeslagen

Jdbi ondersteunt zowel positionele als benoemde parameters.

We voegen positionele parameters in als vraagtekens in een vraag of statement:

Query positionalParamsQuery = handle.createQuery ("selecteer * uit project waarbij naam =?");

Benoemde parameters beginnen in plaats daarvan met een dubbele punt:

Query namedParamsQuery = handle.createQuery ("selecteer * uit project waar url als: patroon");

In beide gevallen gebruiken we een van de varianten van de binden methode:

positionalParamsQuery.bind (0, "tutorials"); namedParamsQuery.bind ("patroon", "% github.com / eugenp /%");

Merk op dat, in tegenstelling tot JDBC, indexen beginnen bij 0.

6.1. Meerdere benoemde parameters tegelijk binden

We kunnen ook meerdere benoemde parameters aan elkaar binden met behulp van een object.

Laten we zeggen dat we deze eenvoudige vraag hebben:

Query query = handle.createQuery ("selecteer id van project waarbij naam =: naam en url =: url"); Mapparams = nieuwe HashMap (); params.put ("naam", "REST met veer"); params.put ("url", "github.com/eugenp/REST-With-Spring");

Dan kunnen we bijvoorbeeld een kaart gebruiken:

query.bindMap (params);

Of we kunnen een object op verschillende manieren gebruiken. Hier binden we bijvoorbeeld een object dat de JavaBean-conventie volgt:

query.bindBean (paramsBean);

Maar we kunnen ook de velden of methoden van een object binden; Zie de Jdbi-documentatie voor alle ondersteunde opties.

7. Complexere verklaringen afgeven

Nu we query's, waarden en parameters hebben gezien, kunnen we teruggaan naar uitspraken en dezelfde kennis toepassen.

Bedenk dat de uitvoeren methode die we eerder zagen, is slechts een handige snelkoppeling.

In feite, net als bij vragen, DDL- en DML-instructies worden weergegeven als instanties van de klasse Bijwerken.

We kunnen er een verkrijgen door de methode aan te roepen createUpdate op een handvat:

Update update = handle.createUpdate ("INVOEGEN IN PROJECT (NAAM, URL) WAARDEN (: naam,: url)");

Vervolgens op een Bijwerken we hebben alle bindmethoden die we hebben in een Vraag, dus sectie 6. is ook van toepassing op updates. url

Verklaringen worden uitgevoerd als we bellen, verrassen, uitvoeren:

int rijen = update.execute ();

Zoals we al hebben gezien, retourneert het het aantal betrokken rijen.

7.1. Kolomwaarden automatisch verhogen

Als een speciaal geval, wanneer we een invoeginstructie hebben met automatisch gegenereerde kolommen (meestal automatisch ophogen of reeksen), willen we misschien de gegenereerde waarden ophalen.

Dan bellen we niet uitvoeren, maar executeAndReturnGeneratedKeys:

Update update = handle.createUpdate ("INVOEGEN IN PROJECT (NAAM, URL)" + "WAARDEN ('tutorials', 'github.com/eugenp/tutorials')"); ResultBearing generatedKeys = update.executeAndReturnGeneratedKeys ();

Resultaat is dezelfde interface geïmplementeerd door de Vraag klasse die we eerder hebben gezien, dus we weten al hoe we het moeten gebruiken:

GeneratedKeys.mapToMap () .findOnly (). get ("id");

8. Transacties

We hebben een transactie nodig wanneer we meerdere instructies moeten uitvoeren als een enkele, atomaire bewerking.

Net als bij verbindingshandvatten, introduceren we een transactie door een methode aan te roepen met een sluiting:

handle.useTransaction ((Handle h) -> {haveFunWith (h);});

En, net als bij handvatten, wordt de transactie automatisch gesloten wanneer de sluiting terugkeert.

We moeten de transactie echter vastleggen of terugdraaien alvorens terug te keren:

handle.useTransaction ((Handle h) -> {h.execute ("..."); h.commit ();});

Als er echter een uitzondering wordt gegenereerd door de sluiting, draait Jdbi de transactie automatisch terug.

Net als bij handvatten hebben we een speciale methode, inTransactie, als we iets willen retourneren van de sluiting:

handle.inTransaction ((Handle h) -> {h.execute ("..."); h.commit (); return true;});

8.1. Handmatig transactiebeheer

Hoewel het in het algemeen niet wordt aanbevolen, kunnen we dat ook beginnen en dichtbij een transactie handmatig:

handle.begin (); // ... handle.commit (); handle.close ();

9. Conclusies en verder lezen

In deze tutorial hebben we de kern van Jdbi geïntroduceerd: vragen, verklaringen en transacties.

We hebben enkele geavanceerde functies weggelaten, zoals aangepaste rij- en kolomtoewijzing en batchverwerking.

We hebben ook geen van de optionele modules besproken, met name de SQL Object-extensie.

Alles wordt in detail gepresenteerd in de Jdbi-documentatie.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in het GitHub-project - dit is een Maven-project, dus het zou gemakkelijk moeten kunnen worden geïmporteerd en uitgevoerd zoals het is.