52周技术系列之Slick 3:面向初学者的函数式关系映射(上)

52周技术系列之Slick 3:面向初学者的函数式关系映射(上)

52-technologies-in-2016 Let's learn a new technology every week. A new technology blog every Sunday in 2016. 52-technologies-in-2016 项目地址: https://gitcode.com/gh_mirrors/52/52-technologies-in-2016

引言

在现代应用开发中,与数据库交互是不可或缺的一部分。传统方式中,我们通常直接编写SQL语句或使用ORM框架。今天我们要介绍的Slick,为Scala开发者提供了一种全新的数据库交互方式——函数式关系映射(FRM)。

Slick概述

Slick(Scala Language-Integrated Connection Kit)是Scala生态中一个强大的关系数据库访问库。与传统的ORM不同,Slick基于函数式编程理念设计,它不会将数据库隐藏在ORM层之后,而是让你能够像操作Scala集合一样操作数据库。

Slick的核心特点

  1. 类型安全:所有查询在编译时进行类型检查
  2. 多数据库支持:同一套代码可生成不同数据库的SQL
  3. 组合性:查询可以像乐高积木一样组合
  4. 异步设计:天然支持响应式编程
  5. 流式处理:内置响应式流支持
  6. 原生SQL支持:必要时可以直接使用SQL

项目搭建

首先创建一个基本的SBT项目,添加以下依赖:

libraryDependencies += "com.typesafe.slick" %% "slick" % "3.1.1"
libraryDependencies += "com.h2database" % "h2" % "1.4.191"
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.1.3"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"

这里我们使用H2作为内存数据库,适合开发和测试环境。

数据模型定义

案例类定义

我们定义一个简单的任务管理模型:

case class Task(
  title: String,
  description: String = "",
  createdAt: LocalDateTime = LocalDateTime.now(),
  dueBy: LocalDateTime,
  tags: Set[String] = Set(),
  id: Long = 0L
)

这个案例类包含了任务的基本属性,其中一些字段有默认值。

表定义

Slick使用Table类来映射数据库表:

class TaskTable(tag: Tag) extends Table[Task](tag, "tasks") {
  def title = column[String]("title")
  def description = column[String]("description")
  def createdAt = column[LocalDateTime]("createdAt")
  def dueBy = column[LocalDateTime]("dueBy")
  def tags = column[Set[String]]("tags")
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  
  override def * = (title, description, createdAt, dueBy, tags, id) <> (Task.tupled, Task.unapply)
}

关键点说明:

  1. Table需要指定存储的类型参数和表名
  2. 每个列定义对应数据库表中的一列
  3. *方法定义了默认投影,将元组与案例类相互转换
  4. id列被定义为自增主键

自定义类型映射

Slick默认不支持Java 8的LocalDateTime和Set[String]类型,我们需要自定义映射:

implicit val localDateTimeColumnType = MappedColumnType.base[LocalDateTime, Timestamp](
  ldt => Timestamp.valueOf(ldt),
  t => t.toLocalDateTime
)

implicit val setStringColumnType = MappedColumnType.base[Set[String], String](
  tags => tags.mkString(","),
  tagsString => tagsString.split(",").toSet
)

数据库操作

创建表

定义创建表的Action:

lazy val Tasks = TableQuery[TaskTable]
val createTaskTableAction = Tasks.schema.create

Action定义后需要通过Database实例执行:

val db = Database.forConfig("taskydb")
val result = Await.result(db.run(DataModel.createTaskTableAction), 2 seconds)

插入数据

插入操作使用++=方法:

def insertTaskAction(tasks: Task*) = Tasks ++= tasks.toSeq

测试用例:

it("should insert single task") {
  val result = Await.result(db.run(
    DataModel.insertTaskAction(Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1)))
  ), 2 seconds)
  result should be(Some(1))
}

查询数据

查询所有任务:

val listTasksAction = Tasks.result

测试用例:

it("should list all tasks") {
  val tasks = Seq(
    Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1)),
    Task(title = "Write blog on Slick", dueBy = LocalDateTime.now().plusDays(2))
  )
  Await.result(db.run(DataModel.insertTaskAction(tasks: _*)), 2 seconds)
  val result = Await.result(db.run(DataModel.listTasksAction), 2 seconds)
  result should have length 2
}

总结

本文介绍了Slick 3的基础用法,包括:

  1. 项目搭建和依赖配置
  2. 数据模型定义和表映射
  3. 自定义类型映射
  4. 基本的CRUD操作

Slick的强大之处在于它将数据库操作抽象为类型安全的Scala集合操作,同时保留了直接控制SQL生成的能力。在后续文章中,我们将深入探讨更高级的查询、事务处理以及性能优化等内容。

对于Scala开发者来说,Slick提供了一种既安全又灵活的方式来处理关系型数据库,是构建现代Scala应用的理想选择。

52-technologies-in-2016 Let's learn a new technology every week. A new technology blog every Sunday in 2016. 52-technologies-in-2016 项目地址: https://gitcode.com/gh_mirrors/52/52-technologies-in-2016

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

任轶眉Tracy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值