0Pricing
Scala for Backend Engineering & Functional Programming · Aula

Integração com bancos de dados usando Slick/Doobie

Aprenda a integrar bancos de dados à sua aplicação Play usando ORMs e bibliotecas populares de Scala, como Slick ou Doobie.

Integração com bancos de dados usando Slick/Doobie é uma aula grátis de Scala for Backend Engineering & Functional Programming no CoddyKit. Esta é a aula 3 de 3. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Scala for Backend Engineering & Functional Programming, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Scala for Backend Engineering & Functional Programming inclui 3 aulas no total.

Por que integrar bancos de dados?

Os aplicativos web frequentemente precisam armazenar e recuperar dados de forma persistente. Esses dados podem ser perfis de usuários, informações de produtos ou publicações de blog.

  • Persistência: os dados continuam disponíveis mesmo após a reinicialização do aplicativo.
  • Escalabilidade: permite lidar com grandes volumes de dados e muitos usuários simultâneos.
  • Confiabilidade: garante a integridade e a possibilidade de recuperação dos dados.

Integrar um banco de dados é essencial para qualquer aplicativo web dinâmico criado com o Play Framework.

Slick e Doobie: ferramentas de banco de dados do Scala

O Scala oferece bibliotecas poderosas para interação com bancos de dados, sendo Slick e Doobie duas opções populares.

  • Slick: uma biblioteca de Mapeamento Relacional Funcional (FRM). Ela permite trabalhar com bancos de dados de forma funcional e com segurança de tipos, abstraindo o SQL.
  • Doobie: uma camada JDBC puramente funcional. Ela oferece controle direto sobre as consultas SQL e, ao mesmo tempo, integração com paradigmas de programação funcional, como o Cats Effect.

Nesta lição, vamos nos concentrar no Slick devido à sua abstração de nível mais alto, que geralmente simplifica a integração inicial com o Play Framework.

Configurando o Slick no Play

Primeiro, adicione o Slick e um driver de banco de dados ao seu build.sbt. Usaremos o H2, um banco de dados em memória, para facilitar a configuração.

Em seguida, configure a conexão com o banco de dados em 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"
)

Configuração do banco de dados

Adicione estas linhas ao seu conf/application.conf para configurar o banco de dados H2 e o perfil do Slick. O slick.dbs.default.profile especifica o tipo de banco de dados.

play.db.prototype.slick define um modelo para as conexões com o banco de dados.

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}
}

Definindo seu modelo (tabela do Slick)

No Slick, você representa as tabelas do banco de dados como classes Scala que estendem slick.jdbc.H2Profile.api.Table. Cada coluna é associada a um tipo Scala.

Definimos uma TableQuery para cada tabela, que é o ponto de entrada para as 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: criação da tabela e do esquema

Este exemplo executável mostra como definir uma tabela User e, em seguida, criar seu esquema em um banco de dados H2 em memória. O método db.run executa ações do banco de dados de forma assí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()
    }
  }
}

Padrão DAO para operações

O padrão Objeto de Acesso a Dados (DAO) ajuda a encapsular as operações do banco de dados. Ele separa a lógica de negócio do seu aplicativo da lógica de persistência.

Um UserDAO conteria métodos para inserir, consultar, atualizar e excluir 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: inserindo e consultando dados

Veja como inserir um novo usuário e, em seguida, consultar todos os usuários do banco de dados usando os métodos do 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()
    }
  }
}

Integrando o DAO ao Play

Em um aplicativo Play, normalmente você injeta seu UserDAO em um controlador usando injeção de dependência. Isso permite que as ações do controlador interajam com o banco de dados.

Você configuraria a DI do Play (por exemplo, o Guice) para fornecer uma instância de Database e de 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")))
  }
}

Verificação rápida: tabela do Slick

Considere a seguinte classe de caso Scala e um mapeamento Table do Slick definido parcialmente:

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

Qual linha completa corretamente a projeção * da tabela Product, supondo que as colunas id, name e price estejam definidas?

Recapitulação: integração com banco de dados

Nesta lição, exploramos como integrar bancos de dados aos seus aplicativos Play Framework usando o Slick.

  • Aprendemos sobre o papel do Slick como uma biblioteca de Mapeamento Relacional Funcional (FRM).
  • Abordamos a configuração das dependências do Slick e do banco de dados em application.conf.
  • Você viu como definir classes de caso Scala e associá-las a tabelas do banco de dados usando a classe Table do Slick.
  • Apresentamos o padrão DAO para organizar as operações do banco de dados e fornecemos exemplos de inserção e consulta de dados.
  • Por fim, vimos como integrar essas operações de DAO aos controladores do Play para criar endpoints de API web.

Dominar a integração com bancos de dados é essencial para criar aplicativos Play robustos e orientados por dados!

Perguntas Frequentes

A aula “Integração com bancos de dados usando Slick/Doobie” é grátis?

Sim — o texto completo de “Integração com bancos de dados usando Slick/Doobie” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Scala for Backend Engineering & Functional Programming, atualize para CoddyKit PRO. O curso de Scala for Backend Engineering & Functional Programming inclui 3 aulas no total.

O que vou aprender em “Integração com bancos de dados usando Slick/Doobie”?

Aprenda a integrar bancos de dados à sua aplicação Play usando ORMs e bibliotecas populares de Scala, como Slick ou Doobie. Você pratica Scala for Backend Engineering & Functional Programming com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Scala for Backend Engineering & Functional Programming?

Nenhuma experiência prévia é necessária. Scala for Backend Engineering & Functional Programming no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 3.

Quanto tempo leva a aula “Integração com bancos de dados usando Slick/Doobie”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Scala for Backend Engineering & Functional Programming?

Sim. Cada aula de Scala for Backend Engineering & Functional Programming inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Fundamentos do Play Framework
  2. Construindo APIs RESTful com Play
  3. Integração com bancos de dados usando Slick/Doobie
← Voltar para Scala for Backend Engineering & Functional Programming