Scala for Backend Engineering & Functional Programming · Lezione

Connettersi con un transactor

Configuri l'accesso al database.

Lezione 1 di 413 passaggi

Connettersi con un transactor è una lezione Scala for Backend Engineering & Functional Programming gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Scala for Backend Engineering & Functional Programming, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Che cos’è un Transactor?

Doobie è un layer JDBC funzionale puro per Scala. Un Transactor[F] fa da ponte tra i programmi puri di Doobie e una connessione reale al database.

Un Transactor sa come acquisire una Connection JDBC, eseguire il programma all’interno di una transazione e rilasciare le risorse in sicurezza.

È parametrizzato da un tipo di effetto F[_], come cats.effect.IO.

import doobie._
import cats.effect.IO

val xa: Transactor[IO] = ???

Il programma ConnectionIO

Le query Doobie sono valori di tipo ConnectionIO[A]. Descrivono un calcolo che richiede una connessione JDBC, ma non lo eseguono ancora.

Si costruiscono questi programmi in modo puro, poi li si passa a un Transactor per eseguirli. Il database non viene toccato finché non si avvia l’esecuzione.

import doobie.implicits._

val program: ConnectionIO[Int] =
  sql"select 42".query[Int].unique

DriverManagerTransactor

Il Transactor più semplice è Transactor.fromDriverManager. Apre una connessione completamente nuova per ogni transazione usando il DriverManager di JDBC.

Richiede il nome della classe del driver, l’URL JDBC, il nome utente e la password. È adatto a test e script, ma non dispone di connection pooling.

val xa = Transactor.fromDriverManager[IO](
  driver = "org.postgresql.Driver",
  url = "jdbc:postgresql://localhost:5432/app",
  user = "postgres",
  password = "secret",
  logHandler = None
)

Esecuzione di un programma

Per eseguire un ConnectionIO si chiama .transact(xa). Si ottiene così un F[A], in questo caso un IO[A].

Il Transactor racchiude il programma in una transazione: esegue il commit in caso di successo e il rollback in caso di errore, rilasciando comunque la connessione.

import doobie.implicits._

val result: IO[Int] =
  sql"select 42".query[Int].unique.transact(xa)

Pooling con HikariCP

In produzione è consigliabile usare un pool di connessioni. Doobie include HikariTransactor, basato su HikariCP.

Viene creato come una Resource, così il pool viene arrestato correttamente. È inoltre necessario passare un execution context per le operazioni JDBC bloccanti.

import doobie.hikari.HikariTransactor
import cats.effect.IO

val xaRes: Resource[IO, HikariTransactor[IO]] =
  HikariTransactor.newHikariTransactor[IO](
    "org.postgresql.Driver",
    "jdbc:postgresql://localhost/app",
    "postgres", "secret",
    connectEC
  )

Perché una Resource?

Un Transactor con pooling gestisce uno stato di lunga durata: connessioni aperte e thread in background. Resource[F, A] garantisce che acquisizione e rilascio siano sempre associati, anche in caso di errori o cancellazione.

In genere si costruisce il Transactor una sola volta all’avvio e lo si riutilizza per tutta la vita dell’applicazione.

xaRes.use { xa =>
  program.transact(xa)
}

Il pool connectEC

HikariCP ha bisogno di un ExecutionContext per attendere le connessioni del pool. Doobie fornisce ExecutionContexts.fixedThreadPool a questo scopo, anch’esso come Resource.

Mantenere questo pool separato da quello per i calcoli impedisce che l’acquisizione bloccante delle connessioni esaurisca le risorse destinate al lavoro legato alla CPU.

import doobie.util.ExecutionContexts

val poolRes =
  for {
    ec <- ExecutionContexts.fixedThreadPool[IO](8)
    xa <- HikariTransactor.newHikariTransactor[IO](
            "org.postgresql.Driver", url, user, pass, ec)
  } yield xa

Configurazione di un DataSource esistente

Se dispone già di un javax.sql.DataSource configurato (ad esempio un’istanza Hikari ottimizzata), lo avvolga direttamente con Transactor.fromDataSource.

È una soluzione comune quando un framework gestisce il pool e si desidera soltanto utilizzarlo con Doobie.

val xa = Transactor.fromDataSource[IO](
  dataSource = myDataSource,
  connectEC = connectEC
)

Smoke test della connessione

Un semplice controllo di salute consiste nell’eseguire select 1 tramite il Transactor. Se la query restituisce un risultato, driver, URL e credenziali funzionano correttamente.

È un buon primo passo prima di collegare query reali.

val check: IO[Int] =
  sql"select 1".query[Int].unique.transact(xa)

// check.unsafeRunSync() == 1

Internals del Transactor: Strategy

Un Transactor è costruito a partire da interpreti e da una Strategy. La Strategy controlla ciò che accade intorno a ogni transazione: before, after, oops (in caso di errore) e always.

La strategia predefinita disattiva l’auto-commit, esegue il commit dopo il successo, il rollback in caso di errore e chiude sempre la connessione.

import doobie.util.transactor.Strategy

val noCommit = xa.copy(
  strategy0 = Strategy.default.copy(after = doobie.free.connection.unit)
)

Logging e osservabilità

Doobie può registrare ogni statement, i relativi argomenti e i tempi di esecuzione. Le versioni moderne associano un LogHandler[F] a ogni query anziché a ogni Transactor.

È prezioso per individuare query lente e verificare il binding dei parametri in produzione.

import doobie.util.log._

val handler: LogHandler[IO] = (ev: LogEvent) =>
  IO.println(ev.sql)

Verifica rapida

Verifichi la Sua comprensione dei Transactor.

Riepilogo

Un Transactor[F] collega i programmi puri ConnectionIO a un database reale. fromDriverManager è adatto ai test; HikariTransactor e fromDataSource sono adatti alla produzione grazie al pooling.

Costruisca i Transactor con pooling come Resource, esegua i programmi con .transact(xa) e lasci che la Strategy predefinita gestisca commit, rollback e pulizia.

Gratis per iniziare

Impara Scala con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
39
Lezioni
143

Domande Frequenti

La lezione «Connettersi con un transactor» è gratuita?

Sì — il testo completo di «Connettersi con un transactor» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Scala for Backend Engineering & Functional Programming, passa a CoddyKit PRO. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Cosa imparerò in «Connettersi con un transactor»?

Configuri l'accesso al database. Eserciti Scala for Backend Engineering & Functional Programming con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Scala for Backend Engineering & Functional Programming?

Non è richiesta alcuna esperienza precedente. Scala for Backend Engineering & Functional Programming su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Connettersi con un transactor»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Scala for Backend Engineering & Functional Programming?

Sì. Ogni lezione Scala for Backend Engineering & Functional Programming include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Connettersi con un transactor
  2. Eseguire query
  3. Inserimenti e aggiornamenti
  4. Comporre transazioni
← Torna a Scala for Backend Engineering & Functional Programming