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
Tabledo 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
- Fundamentos do Play Framework
- Construindo APIs RESTful com Play
- Integração com bancos de dados usando Slick/Doobie