Java >> Java tutorial >  >> Java

Bliv en Master of Java Streams – Del 4:Database Streams

SQL har altid været et deklarativt sprog, hvorimod Java i lang tid har været bydende nødvendigt. Java-streams har ændret spillet. Kod dig vej gennem denne praktiske lab-artikel, og lær hvordan Java-streams kan bruges til at udføre deklarative forespørgsler til en RDBMS-database uden at skrive en enkelt linje SQL-kode. Du vil opdage, at der er en bemærkelsesværdig lighed mellem verberne i Java-streams og SQL-kommandoer.

Denne artikel er den fjerde ud af fem, suppleret med et GitHub-lager, der indeholder instruktioner og øvelser til hver enhed.

Del 1:Oprettelse af streams
Del 2:Mellemliggende operationer
Del 3:Terminaldrift
Del 4:Databasestrømme
Del 5:Oprettelse af en databaseapplikation ved hjælp af streams

Databasestrømme

Da du gjorde dig bekendt med driften af ​​Streams, har du muligvis bemærket en lighed med SQL-konstruktionerne. Nogle af dem har mere eller mindre en direkte tilknytning til Stream-operationer, såsom LIMIT og COUNT . Denne lighed udnyttes af open source-projektet Speedment til at give typesikker adgang til enhver relationel database, der bruger ren Java.

Vi er bidragydere til Speedment open source-projektet, og vi vil beskrive, hvordan Speedment giver os mulighed for at bruge en database som streamkilde og fodre pipelinen med rækker fra enhver af databasetabellerne.

Som afbildet i visualiseringen ovenfor, vil Speedment etablere en forbindelse til databasen og kan derefter videregive data til applikationen. Det er ikke nødvendigt at skrive nogen kode til databaseposterne, da Speedment analyserer den underliggende database og genererer automatisk alle de nødvendige entitetsklasser til domænemodellen. Det sparer en masse tid, når du ikke behøver at skrive og vedligeholde enhedsklasser i hånden for hver tabel, du vil bruge.

Sakila-database

Af hensyn til denne artikel, samt øvelserne, bruger vi MySQL-eksempeldatabasen Sakila som vores datakilde. Sakila-databasen modellerer en gammeldags filmudlejningsvirksomhed og indeholder derfor tabeller som film og skuespiller. En forekomst af databasen er implementeret i skyen og er åben for offentlig adgang.

Speedment Manager

I Speedment kaldes håndtaget til en databasetabel a
Manager . Lederne er en del af den automatisk genererede kode.

En Manager fungerer som et håndtag til en databasetabel og kan fungere som en strømkilde. I dette tilfælde svarer hver række til en forekomst af Film.

En Manager i Speedment instansieres ved at kalde:

1 FilmManager films = speedment.getOrThrow(FilmManager. class );

Bemærk:speeding er en instans, der kan fås fra en ApplicationBuilder (mere om dette emne i næste artikel).

Hvis FilmManager::stream kaldes, er resultatet en Stream som vi frit kan anvende mellem- eller terminaloperationer på. Til at begynde med samler vi alle rækker på en liste.

1 List<Film> allFilms = films.stream().collect(toList());
1234 FilmImpl { filmId = 1 , title = ACADEMY DINOSAUR, … FilmImpl { filmId = 2 , title = ACE GOLDFINGER, … FilmImpl { filmId = 3 , title = ADAPTATION HOLES, …

Filtrering og optælling

Lad os se på et simpelt eksempel, der viser antallet af film med vurderingen "PG-13". Ligesom en almindelig Stream , kan vi filtrere filmene fra med den korrekte bedømmelse og derefter tælle disse poster.

123 long pg13FilmCount = films.stream()     .filter(Film.RATING.equal( "PG-13" ))     .count();
1 pg13FilmCount: 195

En vigtig egenskab, der følger med Speedments tilpassede implementering af Streams, er, at streams er i stand til at optimere deres egen pipeline ved introspektion. Det kan se ud som om Stream vil iterere over alle rækker i en tabel, men dette er ikke tilfældet. I stedet er Speedment i stand til at oversætte pipelinen til en optimeret SQL-forespørgsel, der sendes videre til databasen. Dette betyder, at kun relevante databaseposter trækkes ind i strømmen. Således vil streamen i eksemplet ovenfor automatisk blive gengivet til SQL svarende til "SELECT ... FROM film WHERE rating ='PG-13'"

Denne introspektion kræver, at enhver brug af anonyme lambdaer (som ikke indeholder nogen metadata, der relaterer til den målrettede kolonne) erstattes med prædikater fra Speedment Fields. I dette tilfælde Film.RATING.equal(“PG-13”) returnerer en Predicate som vil blive testet på hver film og returneres sandt, hvis og kun hvis den film har en vurdering, der er PG-13.

Selvom dette ikke forhindrer os i at udtrykke prædikatet som:

1 .filter(f -> f.getRating().equals(“PG- 13 ”))

men dette ville tvinge Speedment til at hente alle rækkerne i tabellen og derefter anvende prædikatet, derfor er det ikke anbefalede.

Find den længste film

Her er et eksempel, der finder den længste film i databasen ved hjælp af max-operatoren med Field Film.LENGTH :

12 Optional<Film> longestFilm = films.stream()     .max(Film.LENGTH);
12 longestFilm: Optional[FilmImpl {filmId = 141 , title = CHICAGO NORTH, length = 185 , ...}]

Sådan finder du tre kortfilm

At finde tre kortfilm (vi definerede korte som <=50 minutter) kan gøres ved at bortfiltrere alle film, der er 50 minutter eller kortere, og vælge de tre første resultater. Prædikatet i eksemplet ser på værdien af ​​kolonnen "længde" og bestemmer, om den er mindre end eller lig med 50.

1234 List<Film> threeShortFilms = films.stream()   .filter(Film.LENGTH.lessOrEqual( 50 ))   .limit( 3 )   .collect(toList());
1234 threeShortFilms: [      FilmImpl { filmId = 2 , length = 48 ,..},      FilmImpl { filmId = 3 , length = 50 , … },      FilmImpl { filmId = 15 , length = 46 , ...}]

Søgning med sortering

Hvis vi skulle vise alle filmene på en hjemmeside eller i en applikation, ville vi nok foretrække at paginere emnerne frem for at indlæse (muligvis) tusindvis af poster på én gang. Dette kan opnås ved at kombinere operationen skip() og limit() . I eksemplet nedenfor indsamler vi indholdet af den anden side, forudsat at hver "side" indeholder 25 poster. Husk på, at Streams ikke garanterer en bestemt rækkefølge af elementerne, hvilket betyder, at vi skal definere en ordre hos den sorterede operatør for at dette fungerer efter hensigten.

12345 List<Film> filmsSortedByLengthPage2 = films.stream()   .sorted(Film.LENGTH)   .skip( 25 * 1 )   .limit( 25 )   .collect(toList());
12 filmsSortedByLengthPage2: [FilmImpl { filmId = 430 , length = 49 , …}, …]

Bemærk:At finde indholdet på den n:te side sker ved at springe over (25 * (n-1)).

Note2:Denne stream vil automatisk blive gengivet til noget som "VÆLG ... FRA film BESTIL EFTER længde ASC LIMIT ? OFFSET ?, værdier:[25, 25]”

Film der starter med "A" sorteret efter længde

Vi kan nemt finde alle film, der starter med det store bogstav "A" og sortere dem efter deres længde (med den korteste film først) sådan her:

1234 List<Film> filmsTitleStartsWithA = films.stream()   .filter(Film.TITLE.startsWith( "A" ))   .sorted(Film.LENGTH)   .collect(Collectors.toList());
1234 filmsTitleStartsWithA: [    FilmImpl { filmId= 15 , title=ALIEN CENTER, …, rating=NC- 17 , length = 46 ,    FilmImpl { filmId= 2 , title=ACE GOLDFINGER, …, rating=G, length = 48 , … ]

Beregningsfrekvenstabeller for filmlængde

Vi kan også bruge groupingBy-operatøren til at sortere filmene i spande afhængigt af deres længde og tælle det samlede antal film i hver spand. Dette vil skabe en såkaldt frekvenstabel over filmlængde.

12345 Map<Short, Long> frequencyTableOfLength = films.stream()   .collect(Collectors.groupingBy(    Film.LENGTH.asShort(),    counting()   ));
1 frequencyTableOfLength: { 46 = 5 , 47 = 7 , 48 = 11 , 49 = 5 , … }

Øvelser

Til denne uges øvelser behøver du ikke bekymre dig om at forbinde din egen database. I stedet har vi allerede leveret en forbindelse til en forekomst af Sakila-databasen i skyen. Som sædvanligt kan øvelserne placeres i denne GitHub-repo. Indholdet af denne artikel er tilstrækkeligt til at løse den fjerde enhed, som kaldes MyUnit4Database . Den tilsvarende
Unit4Database Interface indeholder JavaDocs, som beskriver den tilsigtede implementering af metoderne i MyUnit4Database .

123456789 public interface Unit4Database {     /**      * Returns the total number of films in the database.      *      * @param films manager of film entities      * @return the total number of films in the database      */     long countAllFilms(FilmManager films);


De medfølgende tests (f.eks. Unit4MyDatabaseTests ) vil fungere som et automatisk bedømmelsesværktøj, der fortæller dig, om din løsning var korrekt eller ej.

Næste artikel

Indtil videre har vi kun skrabet overfladen af ​​databasestrømme. Den næste artikel giver dig mulighed for at skrive selvstændige databaseapplikationer i ren Java. Glad kodning!

Forfattere

Per Minborg

Julia Gustafsson

Ressourcer

GitHub Opensource Project Speedment

Speedment Stream ORM Initializer
GitHub Repository  "hol-streams"
Artikel  Del 1:Oprettelse af streams
Artikel Del 2:Mellemliggende operationer
Artikel Del 3:Terminaldrift

Java tag