0Pricing
Scala for Backend Engineering & Functional Programming · Lección

Integración de bases de datos con Slick/Doobie

Aprenda a integrar bases de datos en su aplicación de Play mediante ORMs o bibliotecas populares de Scala, como Slick o Doobie.

Integración de bases de datos con Slick/Doobie es una lección gratuita de Scala for Backend Engineering & Functional Programming en CoddyKit. Esta es la lección 3 de 3. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Scala for Backend Engineering & Functional Programming, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Scala for Backend Engineering & Functional Programming incluye 3 lecciones en total.

¿Por qué integrar bases de datos?

Las aplicaciones web suelen necesitar almacenar y recuperar datos de forma persistente. Estos datos pueden ser perfiles de usuario, información de productos o publicaciones de blog.

  • Persistencia: Los datos siguen disponibles incluso después de reiniciar la aplicación.
  • Escalabilidad: Permite gestionar grandes cantidades de datos y muchos usuarios simultáneos.
  • Fiabilidad: Garantiza la integridad de los datos y la capacidad de recuperación.

Integrar una base de datos es fundamental para cualquier aplicación web dinámica creada con Play Framework.

Slick y Doobie: herramientas de bases de datos de Scala

Scala ofrece bibliotecas potentes para interactuar con bases de datos; Slick y Doobie son dos opciones populares.

  • Slick: Una biblioteca de Functional Relational Mapping (FRM). Permite trabajar con bases de datos de forma funcional y segura con respecto a los tipos, abstrayendo SQL.
  • Doobie: Una capa JDBC puramente funcional. Le proporciona control directo sobre las consultas SQL y, al mismo tiempo, se integra con paradigmas de programación funcional como Cats Effect.

En esta lección nos centraremos en Slick debido a su abstracción de alto nivel, que suele simplificar la integración inicial con Play Framework.

Configurar Slick en Play

Primero, añada Slick y un controlador de base de datos a su build.sbt. Utilizaremos H2, una base de datos en memoria, para facilitar la configuración.

A continuación, configure la conexión a la base de datos en conf/application.conf.

import play.sbt.PlayImport._

libraryDependencies ++= Seq(
  jdbc,
  ws,
  "com.typesafe.play" %% "play-slick" % "5.0.0",
  "com.typesafe.play" %% "play-slick-evolutions" % "5.0.0",
  "com.h2database" % "h2" % "2.2.224"
)

Configuración de la base de datos

Añada estas líneas a conf/application.conf para configurar la base de datos H2 y el perfil de Slick. slick.dbs.default.profile especifica el tipo de base de datos.

play.db.prototype.slick define una plantilla para las conexiones a la base de datos.

slick.dbs.default.profile="slick.jdbc.H2Profile$"

play.db { 
  prototype.slick = {
    db.driver="org.h2.Driver"
    db.url="jdbc:h2:mem:play;DB_CLOSE_DELAY=-1"
    db.username="sa"
    db.password=""
  }

  # Apply the prototype to our default DB
  default = ${play.db.prototype.slick}
}

Definir el modelo (tabla de Slick)

En Slick, las tablas de la base de datos se representan como clases de Scala que extienden slick.jdbc.H2Profile.api.Table. Cada columna se asigna a un tipo de Scala.

Definimos un TableQuery para cada tabla, que es el punto de entrada para realizar consultas.

import slick.jdbc.H2Profile.api._

// Define a case class for our data model
case class User(id: Option[Long], name: String, email: String)

// Define the table mapping
class Users(tag: Tag) extends Table[User](tag, "users") {
  def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def email = column[String]("email")
  
  // Every table needs a * projection with the default mapping
  def * = (id, name, email) <> (User.tupled, User.unapply)
}

// TableQuery for accessing the Users table
val users = TableQuery[Users]

Código: crear la tabla y el esquema

Este ejemplo ejecutable muestra cómo definir una tabla User y crear después su esquema en una base de datos H2 en memoria. El método db.run ejecuta acciones de base de datos de forma asíncrona.

import slick.jdbc.H2Profile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._

case class User(id: Option[Long], name: String, email: String)

class Users(tag: Tag) extends Table[User](tag, "users") {
  def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def email = column[String]("email")
  def * = (id, name, email) <> (User.tupled, User.unapply)
}

object SlickDemo {
  val users = TableQuery[Users]

  def main(args: Array[String]): Unit = {
    val db = Database.forURL("jdbc:h2:mem:test1;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
    try {
      val setup = DBIO.seq(
        users.schema.create
      )
      val future = db.run(setup)
      Await.result(future, 2.seconds)
      println("Users table created successfully!")
    } finally {
      db.close()
    }
  }
}

Patrón DAO para las operaciones

El patrón Data Access Object (DAO) ayuda a encapsular las operaciones de la base de datos. Separa la lógica de negocio de su aplicación de la lógica de persistencia.

Un UserDAO contendría métodos para insertar, consultar, actualizar y eliminar objetos User.

import slick.jdbc.H2Profile.api._
import scala.concurrent.Future

class UserDAO(db: Database) {
  import SlickDemo.users // Assuming users TableQuery is in SlickDemo object

  def insert(user: User): Future[User] = {
    db.run(users returning users.map(_.id) into ((user, id) => user.copy(id = id)) += user)
  }

  def findAll(): Future[Seq[User]] = {
    db.run(users.result)
  }

  def findById(id: Long): Future[Option[User]] = {
    db.run(users.filter(_.id === id).result.headOption)
  }
}

Código: insertar y consultar datos

Así puede insertar un usuario nuevo y consultar después todos los usuarios de la base de datos mediante los métodos del DAO que acabamos de definir.

import slick.jdbc.H2Profile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._

case class User(id: Option[Long], name: String, email: String)

class Users(tag: Tag) extends Table[User](tag, "users") {
  def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def email = column[String]("email")
  def * = (id, name, email) <> (User.tupled, User.unapply)
}

class UserDAO(db: Database) {
  val users = TableQuery[Users]
  def insert(user: User): Future[User] = 
    db.run(users returning users.map(_.id) into ((u, id) => u.copy(id = id)) += user)
  def findAll(): Future[Seq[User]] = 
    db.run(users.result)
}

object SlickOperations {
  def main(args: Array[String]): Unit = {
    val db = Database.forURL("jdbc:h2:mem:test2;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
    val userDAO = new UserDAO(db)
    try {
      Await.result(db.run(userDAO.users.schema.create), 2.seconds)
      println("Table created.")

      val future = for {
        user1 <- userDAO.insert(User(None, "Alice", "alice@example.com"))
        user2 <- userDAO.insert(User(None, "Bob", "bob@example.com"))
        allUsers <- userDAO.findAll()
      } yield allUsers

      val result = Await.result(future, 5.seconds)
      println(s"All users: $result")

    } finally {
      db.close()
    }
  }
}

Integración de DAO en Play

En una aplicación de Play, normalmente inyecta su UserDAO en un controlador mediante inyección de dependencias. Esto permite que las acciones del controlador interactúen con la base de datos.

Deberá configurar la DI de Play (por ejemplo, Guice) para proporcionar una instancia de Database y UserDAO.

import play.api.mvc._
import play.api.libs.json._
import scala.concurrent.ExecutionContext
import javax.inject._

// Assuming User and UserDAO are defined as before

@Singleton
class UserController @Inject()(cc: ControllerComponents, userDAO: UserDAO)
                              (implicit ec: ExecutionContext) extends AbstractController(cc) {

  def listUsers = Action.async {
    userDAO.findAll().map { users =>
      Ok(Json.toJson(users))
    }
  }

  def createUser = Action.async(parse.json) {
    request =>
      request.body.validate[User].map { user =>
        userDAO.insert(user).map(u => Created(Json.toJson(u)))
      }.getOrElse(Future.successful(BadRequest("Invalid User Data")))
  }
}

Comprobación rápida: tabla de Slick

Considere la siguiente clase de caso de Scala y un mapeo de Table de Slick definido parcialmente:

case class Product(id: Option[Int], name: String, price: Double)

¿Qué línea completa correctamente la proyección * de la tabla Product, suponiendo que las columnas id, name y price están definidas?

Resumen: integración de bases de datos

En esta lección, hemos explorado cómo integrar bases de datos en sus aplicaciones de Play Framework mediante Slick.

  • Hemos aprendido cuál es el papel de Slick como biblioteca de mapeo relacional funcional (FRM).
  • Hemos visto cómo configurar las dependencias de Slick y la base de datos en application.conf.
  • Ha aprendido a definir clases de caso de Scala y a mapearlas a tablas de bases de datos mediante la clase Table de Slick.
  • Hemos presentado el patrón DAO para organizar las operaciones de base de datos y hemos mostrado ejemplos de inserción y consulta de datos.
  • Por último, hemos explicado brevemente cómo integrar estas operaciones del DAO en los controladores de Play para crear endpoints de API web.

Dominar la integración de bases de datos es fundamental para crear aplicaciones de Play sólidas y orientadas a los datos.

Preguntas frecuentes

¿La lección «Integración de bases de datos con Slick/Doobie» es gratis?

Sí — el texto completo de «Integración de bases de datos con Slick/Doobie» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Scala for Backend Engineering & Functional Programming, actualiza a CoddyKit PRO. El curso de Scala for Backend Engineering & Functional Programming incluye 3 lecciones en total.

¿Qué aprenderé en «Integración de bases de datos con Slick/Doobie»?

Aprenda a integrar bases de datos en su aplicación de Play mediante ORMs o bibliotecas populares de Scala, como Slick o Doobie. Practicas Scala for Backend Engineering & Functional Programming con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Scala for Backend Engineering & Functional Programming?

No se requiere experiencia previa. Scala for Backend Engineering & Functional Programming en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 3.

¿Cuánto tiempo toma la lección «Integración de bases de datos con Slick/Doobie»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Scala for Backend Engineering & Functional Programming?

Sí. Cada lección de Scala for Backend Engineering & Functional Programming incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Fundamentos de Play Framework
  2. Construcción de API RESTful con Play
  3. Integración de bases de datos con Slick/Doobie
← Volver a Scala for Backend Engineering & Functional Programming