En aquest tema, aprendrem com interactuar amb bases de dades des de Haskell. Utilitzarem la biblioteca persistent per gestionar les connexions i operacions amb bases de dades. Aquest mòdul inclou la configuració de l'entorn, la definició de models, la realització d'operacions bàsiques (CRUD) i exemples pràctics.

Contingut

Introducció a Persistent

persistent és una biblioteca de Haskell que proporciona una interfície per treballar amb bases de dades relacionals i no relacionals. Ofereix una manera senzilla de definir models de dades i realitzar operacions com insercions, actualitzacions, eliminacions i consultes.

Característiques Clau

  • Definició de Models: Utilitza plantilles de Haskell per definir models de dades.
  • Operacions CRUD: Proporciona funcions per a la creació, lectura, actualització i eliminació de registres.
  • Suport per a Diverses Bases de Dades: Compatible amb SQLite, PostgreSQL, MySQL, entre d'altres.

Configuració de l'Entorn

Instal·lació de les Dependències

Per començar, necessitem instal·lar les biblioteques necessàries. Afegiu les següents dependències al vostre fitxer cabal o stack:

-- file: package.yaml (per a Stack)
dependencies:
- persistent
- persistent-sqlite
- persistent-template
- persistent-postgresql
- persistent-mysql
- persistent-mongoDB
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached
- persistent-redis
- persistent-memcached

-- file: package.yaml (per a Stack) dependencies:

  • persistent
  • persistent-sqlite
  • persistent-template
### Configuració de la Base de Dades

Per aquest exemple, utilitzarem SQLite. Creeu un fitxer `config/settings.yml` amb la configuració de la base de dades:

sqlite: database: "database.sqlite3" poolsize: 10

## Definició de Models

Els models es defineixen en un fitxer separat utilitzant la sintaxi de plantilles de Haskell. Creeu un fitxer `models/Models.hs` amb el següent contingut:

{-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}

module Models where

import Database.Persist.TH

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| Person name String age Int Maybe deriving Show |]

Aquest codi defineix un model `Person` amb dos camps: `name` (String) i `age` (Int, opcional).

## Operacions Bàsiques (CRUD)

### Connexió a la Base de Dades

Primer, configurem la connexió a la base de dades. Creeu un fitxer `Main.hs` amb el següent contingut:

{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}

import Control.Monad.IO.Class (liftIO) import Database.Persist import Database.Persist.Sqlite import Database.Persist.TH import Models

main :: IO () main = runSqlite "database.sqlite3" $ do runMigration migrateAll liftIO $ putStrLn "Database migrated."

### Inserció de Registres

Afegim una funció per inserir un nou registre a la base de dades:

main :: IO () main = runSqlite "database.sqlite3" $ do runMigration migrateAll liftIO $ putStrLn "Database migrated."

-- Inserir un nou registre
personId <- insert $ Person "John Doe" (Just 30)
liftIO $ putStrLn $ "Inserted person with ID: " ++ show personId
### Lectura de Registres

Afegim una funció per llegir registres de la base de dades:

main :: IO () main = runSqlite "database.sqlite3" $ do runMigration migrateAll liftIO $ putStrLn "Database migrated."

-- Inserir un nou registre
personId <- insert $ Person "John Doe" (Just 30)
liftIO $ putStrLn $ "Inserted person with ID: " ++ show personId

-- Llegir registres
people <- selectList [] [Asc PersonName]
liftIO $ print people
### Actualització de Registres

Afegim una funció per actualitzar un registre existent:

main :: IO () main = runSqlite "database.sqlite3" $ do runMigration migrateAll liftIO $ putStrLn "Database migrated."

-- Inserir un nou registre
personId <- insert $ Person "John Doe" (Just 30)
liftIO $ putStrLn $ "Inserted person with ID: " ++ show personId

-- Llegir registres
people <- selectList [] [Asc PersonName]
liftIO $ print people

-- Actualitzar un registre
update personId [PersonAge =. Just 31]
liftIO $ putStrLn "Updated person's age."
### Eliminació de Registres

Afegim una funció per eliminar un registre:

main :: IO () main = runSqlite "database.sqlite3" $ do runMigration migrateAll liftIO $ putStrLn "Database migrated."

-- Inserir un nou registre
personId <- insert $ Person "John Doe" (Just 30)
liftIO $ putStrLn $ "Inserted person with ID: " ++ show personId

-- Llegir registres
people <- selectList [] [Asc PersonName]
liftIO $ print people

-- Actualitzar un registre
update personId [PersonAge =. Just 31]
liftIO $ putStrLn "Updated person's age."

-- Eliminar un registre
delete personId
liftIO $ putStrLn "Deleted person."
## Exemple Pràctic

Ara que hem vist les operacions bàsiques, creem un exemple pràctic que combina totes aquestes operacions. El nostre objectiu serà gestionar una llista de persones, permetent afegir, llegir, actualitzar i eliminar registres.

### Codi Complet

{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}

import Control.Monad.IO.Class (liftIO) import Database.Persist import Database.Persist.Sqlite import Database.Persist.TH import Models

main :: IO () main = runSqlite "database.sqlite3" $ do runMigration migrateAll liftIO $ putStrLn "Database migrated."

-- Inserir un nou registre
personId <- insert $ Person "John Doe" (Just 30)
liftIO $ putStrLn $ "Inserted person with ID: " ++ show personId

-- Llegir registres
people <- selectList [] [Asc PersonName]
liftIO $ print people

-- Actualitzar un registre
update personId [PersonAge =. Just 31]
liftIO $ putStrLn "Updated person's age."

-- Eliminar un registre
delete personId
liftIO $ putStrLn "Deleted person."
## Exercicis Pràctics

1. **Afegir un Nou Model**: Afegiu un nou model `Car` amb els camps `make` (String) i `year` (Int). Inseriu, llegiu, actualitzeu i elimineu registres de `Car`.
2. **Consultes Avançades**: Creeu una funció que llegeixi totes les persones majors de 25 anys.
3. **Relacions entre Models**: Definiu una relació entre `Person` i `Car` on una persona pot tenir múltiples cotxes. Inseriu registres i realitzeu consultes que mostrin les persones amb els seus cotxes.

### Solucions

1. **Afegir un Nou Model**

-- models/Models.hs {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}

module Models where

import Database.Persist.TH

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| Person name String age Int Maybe deriving Show

Car make String year Int deriving Show |]

2. **Consultes Avançades**

main :: IO () main = runSqlite "database.sqlite3" $ do runMigration migrateAll liftIO $ putStrLn "Database migrated."

-- Inserir registres
_ <- insert $ Person "John Doe" (Just 30)
_ <- insert $ Person "Jane Doe" (Just 20)

-- Llegir persones majors de 25 anys
people <- selectList [PersonAge >. Just 25] [Asc PersonName]
liftIO $ print people
3. **Relacions entre Models**

-- models/Models.hs {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}

module Models where

import Database.Persist.TH

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| Person name String age Int Maybe deriving Show

Car make String year Int ownerId PersonId deriving Show |]

main :: IO () main = runSqlite "database.sqlite3" $ do runMigration migrateAll liftIO $ putStrLn "Database migrated."

-- Inserir registres
personId <- insert $ Person "John Doe" (Just 30)
_ <- insert $ Car "Toyota" 2010 personId
_ <- insert $ Car "Honda" 2015 personId

-- Llegir persones amb els seus cotxes
people <- selectList [] [Asc PersonName]
forM_ people $ \\(Entity personId person) -> do
    liftIO $ print person
    cars <- selectList [CarOwnerId ==. personId] [Asc CarMake]
    liftIO $ print cars
## Conclusió

En aquest tema, hem après a configurar l'entorn per treballar amb bases de dades en Haskell utilitzant la biblioteca `persistent`. Hem vist com definir models, realitzar operacions bàsiques (CRUD) i hem creat un exemple pràctic per gestionar una llista de persones. Els exercicis pràctics proporcionats us ajudaran a consolidar els coneixements adquirits i a explorar funcionalitats més avançades.

En el següent tema, explorarem com realitzar proves i depuració en aplicacions Haskell.
© Copyright 2024. Tots els drets reservats