JDBC med Groovy

1. Introduktion

I denne artikel vil vi se på, hvordan man forespørger relationelle databaser med JDBC ved hjælp af idiomatisk Groovy.

JDBC er, selvom det er relativt lavt, grundlaget for de fleste ORM'er og andre dataadgangsbiblioteker på højt niveau på JVM. Og vi kan selvfølgelig bruge JDBC direkte i Groovy; det har dog en ret besværlig API.

Heldigvis for os bygger Groovy-standardbiblioteket på JDBC for at præsentere en grænseflade, der er ren, enkel og alligevel kraftig. Så vi udforsker Groovy SQL-modulet.

Vi skal se på JDBC i almindelig Groovy og ikke overveje nogen rammer som Spring, som vi har andre guider til.

2. JDBC og Groovy Setup

Vi er nødt til at medtage groovy-sql-modul blandt vores afhængigheder:

 org.codehaus.groovy groovy 2.4.13 org.codehaus.groovy groovy-sql 2.4.13 

Det er ikke nødvendigt at angive det eksplicit, hvis vi bruger groovy-all:

 org.codehaus.groovy groovy-all 2.4.13 

Vi kan finde den nyeste version af groovy, groovy-sql og groovy-all på Maven Central.

3. Oprette forbindelse til databasen

Den første ting, vi skal gøre for at arbejde med databasen, er at oprette forbindelse til den.

Lad os introducere groovy.sql.Sql klasse, som vi bruger til alle operationer i databasen med Groovy SQL-modulet.

En forekomst af Kvm repræsenterer en database, som vi ønsker at arbejde på.

Imidlertid, en forekomst af Kvmer ikke en enkelt databaseforbindelse. Vi taler om forbindelser senere, lad os ikke bekymre os om dem nu; lad os antage, at alt fungerer magisk.

3.1. Angivelse af forbindelsesparametre

I hele denne artikel skal vi bruge en HSQL-database, som er en let relationel DB, der mest bruges i test.

En databaseforbindelse har brug for en URL, en driver og adgangsoplysninger:

Kort dbConnParams = [url: 'jdbc: hsqldb: mem: testDB', bruger: 'sa', adgangskode: '', driver: 'org.hsqldb.jdbc.JDBCDriver']

Her har vi valgt at specificere dem ved hjælp af en Kort, selvom det ikke er det eneste mulige valg.

Vi kan derefter få en forbindelse fra Kvm klasse:

def sql = Sql.newInstance (dbConnParams)

Vi får se, hvordan du bruger det i de følgende afsnit.

Når vi er færdige, skal vi altid frigive tilknyttede ressourcer:

sql.close ()

3.2. Brug af en Datakilde

Det er almindeligt, især i programmer, der kører inde i en applikationsserver, at bruge en datakilde til at oprette forbindelse til databasen.

Når vi vil samle forbindelser eller bruge JNDI, er en datakilde også den mest naturlige mulighed.

Groovy's Kvm klasse accepterer datakilder fint:

def sql = Sql.newInstance (datakilde)

3.3. Automatisk ressourcestyring

Husker at ringe tæt() når vi er færdige med en Kvm eksempel er kedeligt; maskiner husker jo ting meget bedre, end vi gør.

Med Kvm vi kan pakke vores kode i en lukning og få Groovy-opkald tæt() automatisk når kontrol forlader det, selv i tilfælde af undtagelser:

Sql.withInstance (dbConnParams) {Sql sql -> haveFunWith (sql)}

4. Udstedelse af erklæringer mod databasen

Nu kan vi gå videre til de interessante ting.

Den mest enkle og uspecialiserede måde at udsende en erklæring mod databasen på er udføre metode:

sql.execute "Opret tabel PROJECT (id heltal ikke null, navn varchar (50), url varchar (100))"

I teorien fungerer det både til DDL / DML-udsagn og til forespørgsler; den enkle formular ovenfor tilbyder dog ikke en måde at få forespørgselsresultater tilbage. Vi forlader forespørgsler til senere.

Det udføre metoden har flere overbelastede versioner, men igen ser vi på de mere avancerede brugssager af denne og andre metoder i senere sektioner.

4.1. Indsættelse af data

For at indsætte data i små mængder og i enkle scenarier, udføre Metoden diskuteret tidligere er helt i orden.

Men i tilfælde, hvor vi har genereret kolonner (f.eks. Med sekvenser eller automatisk stigning), og vi vil vide de genererede værdier, findes der en dedikeret metode: executeInsert.

Som for udføre, ser vi nu på den mest enkle metodeoverbelastning, der er tilgængelig, hvilket efterlader mere komplekse varianter til et senere afsnit.

Så antag, at vi har en tabel med en primær nøgle til automatisk stigning (identitet i HSQLDB-sprog):

sql.execute "Opret tabel PROJEKT (ID IDENTITET, NAVN VARCHAR (50), URL VARCHAR (100))"

Lad os indsætte en række i tabellen og gemme resultatet i en variabel:

def ids = sql.executeInsert "" "INSERT IN PROJECT (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials')" ""

executeInsert opfører sig nøjagtigt som udføre, men hvad returnerer det?

Det viser sig, at returværdien er en matrix: dens rækker er de indsatte rækker (husk at en enkelt sætning kan få flere rækker til at blive indsat), og dens kolonner er de genererede værdier.

Det lyder kompliceret, men i vores tilfælde, som er langt den mest almindelige, er der en enkelt række og en enkelt genereret værdi:

assertEquals (0, ids [0] [0])

En efterfølgende indsættelse vil returnere en genereret værdi på 1:

ids = sql.executeInsert "" "INSERT IN PROJECT (NAME, URL) VALUES ('REST with Spring', 'github.com/eugenp/REST-With-Spring')" "" assertEquals (1, ids [0] [ 0])

4.2. Opdatering og sletning af data

Tilsvarende findes der en dedikeret metode til modifikation og sletning af data: executeUpdate.

Igen adskiller dette sig fra udføre kun i sin returværdi, og vi vil kun se på den enkleste form.

Returværdien er i dette tilfælde et heltal, antallet af berørte rækker:

def count = sql.executeUpdate ("UPDATE PROJECT SET URL = '//' + URL") assertEquals (2, count)

5. Forespørgsel om databasen

Ting begynder at blive Groovy, når vi spørger til databasen.

Beskæftiger sig med JDBC ResultSet klasse er ikke ligefrem sjovt. Heldigvis for os tilbyder Groovy en god abstraktion over alt dette.

5.1. Iterering af forespørgselsresultater

Mens sløjfer er så gamle ... vi er alle i lukninger i dag.

Og Groovy er her, der passer til vores smag:

sql.eachRow ("VÆLG * FRA PROJEKT") {GroovyResultSet rs -> haveFunWith (rs)}

Det eachRow metoden udsender vores forespørgsel mod databasen og kalder en lukning over hver række.

Som vi kan se, en række er repræsenteret af en forekomst af GroovyResultSet, som er en udvidelse af almindelig gammel ResultSet med et par tilføjede godbidder. Læs videre for at finde mere om det.

5.2. Adgang til resultatsæt

Ud over alle de ResultSet metoder, GroovyResultSet tilbyder et par praktiske hjælpeprogrammer.

Primært udsætter den navngivne egenskaber, der matcher kolonnenavne:

sql.eachRow ("SELECT * FROM PROJECT") {rs -> assertNotNull (rs.name) assertNotNull (rs.URL)}

Bemærk, hvordan ejendomsnavne er store og små bogstaver.

GroovyResultSet giver også adgang til kolonner ved hjælp af et nulbaseret indeks:

sql.eachRow ("SELECT * FROM PROJECT") {rs -> assertNotNull (rs [0]) assertNotNull (rs [1]) assertNotNull (rs [2])}

5.3. Paginering

Vi kan let side resultaterne, dvs. kun indlæse en delmængde startende fra nogle forskydninger op til nogle maksimale antal rækker. Dette er f.eks. Et almindeligt problem i webapplikationer.

eachRow og relaterede metoder har overbelastning, der accepterer en forskydning og et maksimalt antal returnerede rækker:

def offset = 1 def maxResults = 1 def rows = sql.rows ('SELECT * FROM PROJECT ORDER BY NAME', offset, maxResults) assertEquals (1, rows.size ()) assertEquals ('REST med Spring', rækker [0 ].navn)

Her, den rækker metode returnerer en liste med rækker i stedet for at gentage dem som dem eachRow.

6. Parameteriserede forespørgsler og udsagn

Oftere end ikke er forespørgsler og udsagn ikke helt rettet på kompileringstidspunktet; de har normalt en statisk del og en dynamisk del i form af parametre.

Hvis du overvejer at strække sammenkædning, skal du stoppe nu og læse om SQL-injektion!

Vi nævnte tidligere, at de metoder, vi har set i tidligere afsnit, har mange overbelastninger for forskellige scenarier.

Lad os introducere de overbelastninger, der beskæftiger sig med parametre i SQL-forespørgsler og udsagn.

6.1. Strenge med pladsholdere

I stil svarende til almindelig JDBC kan vi bruge positionsparametre:

sql.execute ('INSERT INTO PROJECT (NAME, URL) VALUES (?,?)', 'tutorials', 'github.com/eugenp/tutorials')

eller vi kan bruge navngivne parametre med et kort:

sql.execute ('INSERT INTO PROJECT (NAME, URL) VALUES (: name,: url)', [name: 'REST with Spring', url: 'github.com/eugenp/REST-With-Spring'])

Dette fungerer for udføre, executeUpdate, rækker og eachRow. executeInsert understøtter også parametre, men dens signatur er lidt anderledes og vanskeligere.

6.2. Groovy Strings

Vi kan også vælge en Groovier-stil ved hjælp af GStrings med pladsholdere.

Alle de metoder, vi har set, erstatter ikke pladsholdere i GStrings på den sædvanlige måde; snarere indsætter de dem som JDBC-parametre, hvilket sikrer, at SQL-syntaksen er korrekt bevaret uden behov for at citere eller undslippe noget og dermed ingen risiko for injektion.

Dette er helt fint, sikkert og groovy:

def name = 'REST with Spring' def url = 'github.com/eugenp/REST-With-Spring' sql.execute "INSERT INTO PROJECT (NAME, URL) VALUES ($ {name}, $ {url})"

7. Transaktioner og forbindelser

Indtil videre har vi sprunget over en meget vigtig bekymring: transaktioner.

Faktisk har vi slet ikke talt om, hvordan Groovy er Kvm administrerer forbindelser, enten.

7.1. Kortlevede forbindelser

I de hidtil præsenterede eksempler hver forespørgsel eller erklæring blev sendt til databasen ved hjælp af en ny, dedikeret forbindelse. Kvm lukker forbindelsen, så snart operationen afsluttes.

Selvfølgelig, hvis vi bruger en forbindelsespulje, kan påvirkningen på ydeevnen være lille.

Stadig, hvis vi vil udstede flere DML-udsagn og -forespørgsler som en enkelt atomoperation, vi har brug for en transaktion.

For at en transaktion i første omgang skal være mulig, har vi også brug for en forbindelse, der spænder over flere udsagn og forespørgsler.

7.2. Transaktioner med en cachelagret forbindelse

Groovy SQL tillader os ikke at oprette eller få adgang til transaktioner eksplicit.

I stedet bruger vi medTransaktion metode med lukning:

sql.withTransaction {sql.execute "" "INSERT IN PROJECT (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials')" "" sql.execute "" "INSERT INTO PROJECT (NAME, URL ) VÆRDIER ('HVIL med foråret', 'github.com/eugenp/REST-With-Spring') "" "}

Inde i lukningen bruges en enkelt databaseforbindelse til alle forespørgsler og udsagn.

Desuden begås transaktionen automatisk, når lukningen ophører, medmindre den afsluttes tidligt på grund af en undtagelse.

Vi kan dog også manuelt begå eller tilbageføre den aktuelle transaktion med metoder i Kvm klasse:

sql.withTransaction {sql.execute "" "INSERT IN PROJECT (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials')" "" sql.commit () sql.execute "" "INSERT INTO PROJEKT (NAVN, URL) VÆRDIER ('HVIL med foråret', 'github.com/eugenp/REST-With-Spring') "" "sql.rollback ()}

7.3. Cachede forbindelser uden en transaktion

Endelig bruger vi til at genbruge en databaseforbindelse uden transaktionssemantikken beskrevet ovenfor cacheConnection:

sql.cacheConnection {sql.execute "" "INSERT IN PROJECT (NAME, URL) VALUES ('tutorials', 'github.com/eugenp/tutorials')" "" throw new Exception ('This does not roll back')}

8. Konklusioner og yderligere læsning

I denne artikel har vi set på Groovy SQL-modulet, og hvordan det forbedrer og forenkler JDBC med lukninger og Groovy-strenge.

Vi kan så med sikkerhed konkludere, at almindelig gammel JDBC ser lidt mere moderne ud med et drys Groovy!

Vi har ikke talt om hver eneste funktion i Groovy SQL; for eksempel har vi udeladt batchbehandling, lagrede procedurer, metadata og andre ting.

For yderligere information, se Groovy-dokumentationen.

Implementeringen af ​​alle disse eksempler og kodestykker findes i GitHub-projektet - dette er et Maven-projekt, så det skal være let at importere og køre som det er.


$config[zx-auto] not found$config[zx-overlay] not found