Scala for Backend Engineering & Functional Programming · Lekcja

Wykonywanie zapytań

Odczyta Pan/Pani wiersze do case class.

Lekcja 2 z 413 kroki

Wykonywanie zapytań to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Scala for Backend Engineering & Functional Programming, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.

Interpolacja sql

Interpolator napisów sql biblioteki Doobie stanowi podstawę wykonywania zapytań. Analizuje SQL i zamienia osadzone wartości Scali na bezpiecznie wiązane parametry.

Interpolowane wartości stają się symbolami zastępczymi JDBC ?, dzięki czemu nie występuje ryzyko SQL injection i nie trzeba ręcznie konfigurować PreparedStatement.

import doobie.implicits._

val minAge = 18
val frag = sql"select name from users where age >= $minAge"

Od Fragment do Query

Wyrażenie sql"..." jest obiektem Fragment. Aby wykonać je jako zapytanie odczytujące, należy wywołać .query[A], wybierając typ wiersza A.

Doobie używa klasy typów Read[A] do mapowania kolumn wynikowych na A, obsługując typy proste, krotki i klasy przypadków.

case class User(name: String, age: Int)

val q: Query0[User] =
  sql"select name, age from users".query[User]

unique, option i to

Obiekt Query0[A] udostępnia różne akumulatory. .unique oczekuje dokładnie jednego wiersza, .option — zera lub jednego, a .to[List] zbiera wszystkie wiersze.

Każdy z nich zwraca ConnectionIO, na którym później należy wywołać .transact.

val one: ConnectionIO[User] = q.unique
val maybe: ConnectionIO[Option[User]] = q.option
val all: ConnectionIO[List[User]] = q.to[List]

Mapowanie wierszy na klasy przypadków

Doobie automatycznie wyprowadza instancje Read dla klas przypadków, gdy każde pole ma mapowanie kolumny. Kolejność kolumn musi odpowiadać liście wyboru.

Dzięki temu można wybierać dane bezpośrednio do rozbudowanych typów domenowych, bez ręcznego analizowania wierszy.

case class Account(id: Long, email: String, active: Boolean)

val accounts =
  sql"select id, email, active from accounts"
    .query[Account].to[Vector]

Zapytania parametryzowane

Każda interpolowana wartość Scali jest wiązana jako parametr przy użyciu swojej instancji Put. Można interpolować dowolną liczbę wartości, także w różnych miejscach.

Ponieważ wartości są wiązane, a nie konkatenowane, typy są sprawdzane, a wstrzyknięcie kodu jest niemożliwe.

def findByEmail(email: String): ConnectionIO[Option[Account]] =
  sql"select id, email, active from accounts where email = $email"
    .query[Account].option

Strumieniowanie dużych wyników

W przypadku dużych zbiorów wyników należy użyć .stream, aby uzyskać fs2 Stream[ConnectionIO, A]. Wiersze są pobierane leniwie za pomocą kursora po stronie serwera, dzięki czemu zużycie pamięci pozostaje ograniczone.

Można połączyć go z kombinatorami fs2, aby przetwarzać miliony wierszy przyrostowo.

import fs2.Stream

val s: Stream[ConnectionIO, Account] =
  sql"select id, email, active from accounts"
    .query[Account].stream

Kontrolowanie rozmiaru pobierania

Domyślny rozmiar pobierania kursora może w niektórych sterownikach załadować wszystko jednocześnie. .streamWithChunkSize(n) ustawia rozmiar pobierania JDBC, dzięki czemu kursor pobiera po n wierszy w jednym żądaniu.

Dostrojenie tej wartości pozwala zrównoważyć liczbę żądań i zużycie pamięci.

val s =
  sql"select id, email, active from accounts"
    .query[Account]
    .streamWithChunkSize(512)

Opcjonalne kolumny i wartości null

Kolumny dopuszczające wartość null są mapowane w typie wiersza na Option[A]. Jeśli kolumna może zawierać NULL, należy odczytywać ją jako Option, w przeciwnym razie Doobie zgłosi wyjątek dla wartości null.

Dzięki temu wartości null są jawne i bezpieczne typowo na granicy systemu.

case class Person(id: Long, nickname: Option[String])

val ps =
  sql"select id, nickname from people".query[Person].to[List]

Budowanie SQL za pomocą Fragment

Wartości Fragment można łączyć za pomocą ++, składając zapytania z fragmentów przy zachowaniu wiązania parametrów.

Interpolator fr tworzy fragment z końcową spacją, co ułatwia łączenie klauzul.

val base = fr"select id, email, active from accounts"
val where = fr"where active = $${true}"
val q = (base ++ where).query[Account]

Dynamiczne klauzule WHERE

Pomocnicze elementy Fragments w Doobie budują klauzule warunkowe. Fragments.whereAndOpt łączy opcjonalne filtry, emitując tylko te, które są obecne.

Jest to idiomatyczny sposób bezpiecznego budowania dynamicznych zapytań wyszukujących.

import doobie.util.fragments._

val emailOpt: Option[String] = Some("a@b.com")
val filter = whereAndOpt(emailOpt.map(e => fr"email = $e"))
val q = (fr"select id, email, active from accounts" ++ filter)

Sprawdzanie zapytań podczas budowania

Analiza check w Doobie wykonuje zapytanie względem schematu bazy danych, aby zweryfikować zgodność typów i liczby kolumn z typami Scali.

Użyta w testach wykrywa rozbieżności schematu, zanim trafią one do środowiska produkcyjnego.

import doobie.scalatest._

// inside a spec mixing in IOChecker
check(sql"select id, email, active from accounts".query[Account])

Szybkie sprawdzenie

Należy wybrać właściwy akumulator.

Podsumowanie

Interpolator sql buduje parametryzowane obiekty Fragment; .query[A] przekształca je w Query0[A] mapowany za pomocą Read[A].

Należy wybrać .unique, .option, .to[List] lub .stream, aby akumulować wiersze. Elementów Fragments należy używać do dynamicznych klauzul, a check do sprawdzania zgodności ze schematem.

Bezpłatny start

Ucz się Scala dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
39
Lekcje
143

Często zadawane pytania

Czy lekcja „Wykonywanie zapytań” jest bezpłatna?

Tak — pełny tekst „Wykonywanie zapytań” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Scala for Backend Engineering & Functional Programming, przejdź na CoddyKit PRO. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.

Co nauczysz się w „Wykonywanie zapytań”?

Odczyta Pan/Pani wiersze do case class. Ćwiczysz Scala for Backend Engineering & Functional Programming z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Scala for Backend Engineering & Functional Programming?

Nie wymagamy żadnego doświadczenia. Scala for Backend Engineering & Functional Programming w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Wykonywanie zapytań”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Scala for Backend Engineering & Functional Programming?

Tak. Każda lekcja Scala for Backend Engineering & Functional Programming zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Łączenie za pomocą transaktora
  2. Wykonywanie zapytań
  3. Wstawianie i aktualizowanie
  4. Komponowanie transakcji
← Powrót do Scala for Backend Engineering & Functional Programming