de Daniel Newton

O introducere la accesul la baze de date relaționale reactive cu Spring și R2DBC

O introducere la accesul la baze de date relationale reactive
Dig De Pexels

Nu cu mult timp în urmă, a fost lansată o variantă reactivă a driverului JDBC, cunoscută sub numele de R2DBC. Permite transmiterea în mod asincron a datelor către orice puncte finale care s-au abonat la acesta. Folosind un driver reactiv precum R2DBC împreună cu Spring, WebFlux vă permite să scrieți o aplicație completă care gestionează primirea și trimiterea datelor asincron.

În această postare, ne vom concentra pe baza de date, de la conectarea la baza de date și apoi, în cele din urmă, salvarea și recuperarea datelor. Pentru a face acest lucru, vom folosi Spring Data. Ca și în cazul tuturor modulelor Spring Data, acesta ne oferă configurația prealabilă. Aceasta scade cantitatea de cod boilerplate pe care trebuie să o scriem pentru a configura aplicația noastră. Mai mult decât atât, oferă un strat pe driverul bazei de date care face mai ușoară realizarea sarcinilor simple și sarcinile mai dificile puțin mai dureroase.

Pentru conținutul acestei postări, folosesc o bază de date Postgres. La momentul scrierii, numai Postgres, H2 și Microsoft SQL Server au propriile implementări ale driverelor R2DBC.

Am scris anterior două postări despre bibliotecile reactive de date Spring, una pe Mongo și altul despre Cassandra. Este posibil să fi observat că niciuna dintre aceste baze de date nu sunt baze de date RDBMS. Acum există alți drivere reactive disponibile pentru o lungă perioadă de timp (am scris postarea Mongo cu aproape 2 ani în urmă), dar în momentul scrierii unui driver reactiv pentru o bază de date RDBMS este încă un lucru destul de nou. Această postare va urma un format similar cu cele.

Mai mult, am scris și o postare despre utilizarea Spring WebFlux pe care le-am menționat în introducere. Simțiți-vă liber să aruncați o privire la asta dacă sunteți interesat să produceți o aplicație web complet reactivă.

Dependențe

Există câteva lucruri de subliniat aici.

Cu cât folosiți mai mult Spring Boot, cu atât vă veți obișnui să importați un singur spring-boot-starter dependență pentru ceea ce doriți să faceți. De exemplu, am sperat că ar fi existat un spring-boot-starter-r2dbc dependență, dar, din păcate, nu există una. Inca.

Pur și simplu, această bibliotecă este pe partea nouă și, în momentul scrierii, nu are propriul modul Spring Boot care conține dependențe de care are nevoie, împreună cu o configurare mai rapidă prin configurare automată. Sunt sigur că aceste lucruri vor veni la un moment dat și vor facilita configurarea unui driver R2DBC.

Deocamdată, va trebui să completăm manual câteva dependențe suplimentare.

În plus, bibliotecile R2DBC au doar versiuni Milestone (mai multe dovezi că sunt noi), așa că trebuie să ne asigurăm că aducem în depozitul Spring Milestone. Probabil că va trebui să actualizez această postare în viitor, când va primi o versiune de lansare.

Conectarea la baza de date

Datorită faptului că Spring Data face multă treabă pentru noi, singurul Bean care trebuie creat manual este ConnectionFactory care conține detaliile conexiunii bazei de date:

Primul lucru de remarcat aici este extensia AbstractR2dbcConfiguration. Această clasă conține o încărcătură de fasole pe care nu mai trebuie să o creăm manual. Implementare connectionFactory este singura cerință a clasei, deoarece este necesară pentru crearea DatabaseClient Fasole. Acest tip de structură este tipic pentru modulele Spring Data, așa că se simte destul de familiar atunci când încerci altul. În plus, m-aș aștepta ca această configurație manuală să fie eliminată odată ce configurația automată este disponibilă și să fie condusă exclusiv prin intermediul application.properties.

Am inclus port proprietate aici, dar dacă nu v-ați jucat cu configurația dvs. Postgres, atunci vă puteți baza pe valoarea implicită a 5432.

Cele patru proprietăți: host, database, username și password definit de PostgresqlConnectionFactory sunt minimul necesar pentru ca acesta să funcționeze. Cu atât mai puțin și veți experimenta excepții în timpul pornirii.

Folosind această configurație, Spring se poate conecta la o instanță Postgres care rulează.

Ultima informație remarcabilă din acest exemplu este utilizarea @EnableR2dbcRepositories. Această adnotare instruiește Spring să găsească orice interfețe de depozit care extind Spring’s Repository interfață. Aceasta este utilizată ca interfață de bază pentru instrumentarea depozitelor Spring Data. Vom analiza acest lucru puțin mai în detaliu în secțiunea următoare. Informația principală de luat de aici este că trebuie să utilizați @EnableR2dbcRepositories adnotare pentru a valorifica pe deplin capacitățile Spring Data.

Crearea unui depozit de date de primăvară

După cum sa menționat mai sus, în această secțiune vom analiza adăugarea unui depozit de date de primăvară. Aceste depozite sunt o caracteristică frumoasă a Spring Data, ceea ce înseamnă că nu trebuie să scrieți o încărcare de cod suplimentar pentru a scrie pur și simplu o interogare.

Din păcate, cel puțin pentru moment, Spring R2DBC nu poate deduce interogări în același mod în care fac alte module Spring Data (sunt sigur că acest lucru va fi adăugat la un moment dat). Aceasta înseamnă că va trebui să utilizați @Query adnotare și scrieți manual SQL. Hai să aruncăm o privire:

Această interfață se extinde R2dbcRepository. La rândul său, aceasta se extinde ReactiveCrudRepository și apoi până la Repository. ReactiveCrudRepository oferă funcțiile CRUD standard și din ceea ce înțeleg, R2dbcRepository nu oferă funcții suplimentare și este în schimb o interfață creată pentru o mai bună denumire situațională.

R2dbcRepository ia doi parametri generici, unul fiind clasa de entitate pe care o ia ca intrare și o produce ca ieșire. Al doilea fiind tipul Cheii Primare. Prin urmare, în această situație, Person clasa este administrată de PersonRepository (are sens) și câmpul Cheie primară din interior Person este un Int.

Tipurile de funcții returnate din această clasă și cele furnizate de ReactiveCrudRepository sunteți Flux și Mono (nu se vede aici). Acestea sunt tipuri de reactoare de proiect pe care Spring le folosește ca tipuri implicite de flux reactiv. Flux reprezintă un flux de elemente multiple în timp ce a Mono este un singur rezultat.

În cele din urmă, așa cum am menționat anterior exemplului, fiecare funcție este adnotată cu @Query. Sintaxa este destul de simplă, SQL fiind un șir în interiorul adnotării. $1 ($2, $3, etc … pentru mai multe intrări) reprezintă valoarea introdusă în funcție. După ce ați făcut acest lucru, Spring se va ocupa de restul și va transmite intrările în parametrii lor de intrare, va aduna rezultatele și le va mapa la clasa de entitate desemnată de depozit.

O privire foarte rapidă asupra entității

Nu voi spune multe aici, ci pur și simplu arăt Person clasa folosită de PersonRepository.

De fapt, există un punct de făcut aici. id a fost anulat și a furnizat o valoare implicită de null pentru a permite Postgres să genereze următoarea valoare adecvată. Dacă acest lucru nu este anulabil și un id valoarea este furnizată, Spring va încerca de fapt să ruleze o actualizare în loc de o inserare la salvare. Există și alte modalități în acest sens, dar cred că acest lucru este suficient de bun.

Această entitate va mapa la people tabel definit mai jos:

Văzând totul în acțiune

Acum să aruncăm o privire asupra faptului că face ceva. Mai jos este un cod care introduce câteva înregistrări și le recuperează în câteva moduri diferite:

Un lucru pe care îl voi menționa despre acest cod. Există o posibilitate foarte reală de a executa fără a insera sau a citi unele înregistrări. Dar, când te gândești la asta, are sens. Aplicațiile reactive sunt menite să facă lucrurile în mod asincron și, prin urmare, această aplicație a început să proceseze apelurile funcționale în diferite fire. Fără blocarea firului principal, aceste procese asincrone s-ar putea să nu se execute niciodată pe deplin. Din acest motiv, există unele Thread.sleep apeluri în acest cod, dar le-am eliminat din exemplu pentru a menține totul ordonat.

Ieșirea pentru rularea codului de mai sus ar arăta ca cea de mai jos:

[ main] onSubscribe(FluxConcatMap.ConcatMapImmediate)[ main] request(unbounded)[actor-tcp-nio-1] onNext(Person(id=35, name=Dan Newton, age=25))[actor-tcp-nio-1] onNext(Person(id=36, name=Laura So, age=23))[actor-tcp-nio-1] onComplete()[actor-tcp-nio-2] findAll - Person(id=35, name=Dan Newton, age=25)[actor-tcp-nio-2] findAll - Person(id=36, name=Laura So, age=23)[actor-tcp-nio-4] findAllByName - Person(id=36, name=Laura So, age=23)[actor-tcp-nio-5] findAllByAge - Person(id=35, name=Dan Newton, age=25)

Câteva lucruri de luat aici:

  • onSubscribe și request apar pe firul principal unde Flux a fost chemat de la. Numai saveAll generează acest lucru deoarece a inclus fișierul log funcţie. Adăugarea acestuia la celelalte apeluri ar duce la același rezultat al înregistrării la firul principal.
  • Execuția conținută în funcția de subscriere și pașii interni ai Flux sunt rulate pe fire separate.

Acest lucru nu se apropie de o reprezentare reală a modului în care ați utiliza fluxurile reactive într-o aplicație reală, dar, sperăm, demonstrează cum să le utilizați și oferă o perspectivă asupra modului în care acestea se execută.

Concluzie

În concluzie, fluxurile reactive au ajuns la unele baze de date RDBMS datorită driverului R2DBC și Spring Data, care construiește un strat deasupra pentru a face totul puțin mai ordonat. Folosind Spring Data R2DBC suntem capabili să creăm o conexiune la o bază de date și să începem interogarea acesteia fără a fi nevoie de prea mult cod.

Deși primăvara face deja multe pentru noi, ar putea face mai mult. În prezent, nu are suport pentru configurarea automată Spring Boot. Ceea ce este cam enervant. Dar, sunt sigur că cineva va reuși să o facă în curând și va face totul chiar mai bun decât este deja.

Codul folosit în această postare poate fi găsit pe pagina mea GitHub.

Dacă vi s-a părut util acest post, mă puteți urmări pe Twitter la @LankyDanDev pentru a ține pasul cu noile mele postări.

Vezi toate mesajele lui Dan Newton

Publicat inițial la lankydanblog.com pe 16 februarie 2019.