Doobie 更新和插入大小写 class 语法
Doobie update and insert case class syntax
Doobie 可以 select *
使用 case class 来方便和正确地传递参数,但我看不出如何以与 update
和 [=17 类似的方式工作=].
例如,给定这样一个案例 class:
case class Course(
sku: String,
title: String,
id: Id,
price: Int,
instructorid: Id,
groupid: Id,
shortdescription: String = "",
transcript: String = "",
project_home: String = "",
repository: String = "",
category: String = "",
image: String = "",
privacy: String = "",
language: String = "",
keywords: String = "",
goals: String = "",
instructionallevel: String = "",
audience: String = "",
studenttasks: String = "",
sections: String = "",
active: Boolean = true,
video: String = "",
paypal_button_id: String = "",
prerequisite_ids: String = ""
)
我可以很好地select
记录。这种漂亮的语法是可能的,因为 Doobie 遍历 Course
case class 属性并通过将它们的名称与 courses
数据库记录字段匹配来为它们分配值:
def find(id: Id): Option[Course] =
sql"select * from courses where id = $id"
.query[Course]
.option
.transact(SQLSupport.xa)
.unsafeRunSync
但是 insert
需要手动列出所有 case class 属性,并与值匹配,这很糟糕且容易出错:
/** @return saved Course with new Id */
def save(course: Course): Course = {
val insert: doobie.ConnectionIO[Course] = sql"""insert into courses (
sku,
title,
price,
instructorid,
groupid,
shortdescription,
transcript,
project_home,
repository,
category,
image,
privacy,
language,
keywords,
goals,
instructionallevel,
audience,
studenttasks,
sections,
active,
video,
paypal_button_id,
prerequisite_ids
) values (
${ course.sku },
${ course.title },
${ course.price },
${ course.instructorid },
${ course.groupid },
${ course.shortdescription },
${ course.transcript },
${ course.project_home },
${ course.repository },
${ course.category },
${ course.image },
${ course.privacy },
${ course.language },
${ course.keywords },
${ course.goals },
${ course.instructionallevel },
${ course.audience },
${ course.studenttasks },
${ course.sections },
${ course.active },
${ course.video },
${ course.paypal_button_id },
${ course.prerequisite_ids }
)"""
.update
.withUniqueGeneratedKeys("id")
val newCourse: Course = insert.transact(SQLSupport.xa).unsafeRunSync
newCourse
}
还有update
同样可怕:
/** @return updated Course, which should be identical to the given course */
def update(course: Course): Course = {
val update: doobie.ConnectionIO[Course] = sql"""update courses set
sku = ${ course.sku },
title = ${ course.title },
id = ${ course.id },
price = ${ course.price },
instructorid = ${ course.instructorid },
groupid = ${ course.groupid },
shortdescription = ${ course.shortdescription },
transcript = ${ course.transcript },
project_home = ${ course.project_home },
repository = ${ course.repository },
category = ${ course.category },
image = ${ course.image },
privacy = ${ course.privacy },
language = ${ course.language },
keywords = ${ course.keywords },
goals = ${ course.goals },
instructionallevel = ${ course.instructionallevel },
audience = ${ course.audience },
studenttasks = ${ course.studenttasks },
sections = ${ course.sections },
active = ${ course.active },
video = ${ course.video },
paypal_button_id = ${ course.paypal_button_id },
prerequisite_ids = ${ course.prerequisite_ids }
where id = ${ course.id }"""
.update
.withUniqueGeneratedKeys("id")
val modifiedCourse: Course = update.transact(SQLSupport.xa).unsafeRunSync
modifiedCourse
}
有没有更好的方法?
如果您使用的是 Postgres,可以看看 Rob Norris 的另一个库 - skunk。
它允许您编写自定义编解码器:
case class City(id: Int, name: String, code: String, district: String, pop: Int)
val city: Codec[City] =
(int4 ~ varchar ~ bpchar(3) ~ varchar ~ int4).gimap[City]
val insertCity: Command[City] =
sql"""
INSERT INTO city
VALUES ($city)
""".command
Doobie 具有来自 getquill.io 的 Quill 集成,它允许您对 sql DML 的用例进行建模 类
https://github.com/polyvariant/doobie-quill
Doobie
文档非常棒,但有时您可能会发现自己遇到的某些场景并没有在他们的文档中直接解释。
为了直接插入一个 case class
对象(不是它们的属性),你必须定义一个 Write[A]
来告诉 Doobie
必须如何插入数据。当属性映射 class 与数据库 table.
中的属性映射略有不同时使用
想象一下下面的情况class:
case class Course (id: UUID, name: String, year: Int)
在这种情况下,我们需要为doobie定义一个Write[Course]
,即:
// Scala 3:
given Write[Course] = Write[(UUID, String, Int)].contramap(c => (c.id, c.name, c.year))
// Scala 2:
implicit val writer : Write[Course] = Write[(UUID, String, Int)].contramap(c => (c.id, c.name, c.year))
现在,您可以 运行 您的 Update
和 Doobie
将知道如何映射您的列:
def insertCourse(course: Course): Update0 =
sql"""INSERT INTO courses (id, name, year) VALUES ($course)""".update
此外,您可能需要这些导入:
import doobie.implicits.*
import doobie.implicits.javasql.*
import doobie.postgres.implicits.*
import doobie.*
如果您的 case class
属性及其类型与数据库 table 中指定的完全匹配,则无需手动指定 Writer[Course]
因为 Doobie 会自动为你推导它 [1] 这应该对你有用:
case class Course (id: UUID, name: String, year: Int)
def insertCourse(course: Course): Update0 =
sql"""INSERT INTO courses (id, name, year) VALUES ($course)""".update
Credits to my partner Y.C. that helped me to resolve this issue!
小提示:万一大家也遇到了呢
doobie.syntax.SqlInterpolator.SingleFragment[_]; incompatible interpolation method sql
以 David Corral 为例:
而不是
def insertCourse(course: Course): Update0 =
sql"""INSERT INTO courses (id, name, year) VALUES ($course)""".update
尝试在片段中包装课程变量
def insertCourse(course: Course): Update0 =
sql"""INSERT INTO courses (id, name, year) VALUES (${Fragments.values(course)})""".update
Doobie 可以 select *
使用 case class 来方便和正确地传递参数,但我看不出如何以与 update
和 [=17 类似的方式工作=].
例如,给定这样一个案例 class:
case class Course(
sku: String,
title: String,
id: Id,
price: Int,
instructorid: Id,
groupid: Id,
shortdescription: String = "",
transcript: String = "",
project_home: String = "",
repository: String = "",
category: String = "",
image: String = "",
privacy: String = "",
language: String = "",
keywords: String = "",
goals: String = "",
instructionallevel: String = "",
audience: String = "",
studenttasks: String = "",
sections: String = "",
active: Boolean = true,
video: String = "",
paypal_button_id: String = "",
prerequisite_ids: String = ""
)
我可以很好地select
记录。这种漂亮的语法是可能的,因为 Doobie 遍历 Course
case class 属性并通过将它们的名称与 courses
数据库记录字段匹配来为它们分配值:
def find(id: Id): Option[Course] =
sql"select * from courses where id = $id"
.query[Course]
.option
.transact(SQLSupport.xa)
.unsafeRunSync
但是 insert
需要手动列出所有 case class 属性,并与值匹配,这很糟糕且容易出错:
/** @return saved Course with new Id */
def save(course: Course): Course = {
val insert: doobie.ConnectionIO[Course] = sql"""insert into courses (
sku,
title,
price,
instructorid,
groupid,
shortdescription,
transcript,
project_home,
repository,
category,
image,
privacy,
language,
keywords,
goals,
instructionallevel,
audience,
studenttasks,
sections,
active,
video,
paypal_button_id,
prerequisite_ids
) values (
${ course.sku },
${ course.title },
${ course.price },
${ course.instructorid },
${ course.groupid },
${ course.shortdescription },
${ course.transcript },
${ course.project_home },
${ course.repository },
${ course.category },
${ course.image },
${ course.privacy },
${ course.language },
${ course.keywords },
${ course.goals },
${ course.instructionallevel },
${ course.audience },
${ course.studenttasks },
${ course.sections },
${ course.active },
${ course.video },
${ course.paypal_button_id },
${ course.prerequisite_ids }
)"""
.update
.withUniqueGeneratedKeys("id")
val newCourse: Course = insert.transact(SQLSupport.xa).unsafeRunSync
newCourse
}
还有update
同样可怕:
/** @return updated Course, which should be identical to the given course */
def update(course: Course): Course = {
val update: doobie.ConnectionIO[Course] = sql"""update courses set
sku = ${ course.sku },
title = ${ course.title },
id = ${ course.id },
price = ${ course.price },
instructorid = ${ course.instructorid },
groupid = ${ course.groupid },
shortdescription = ${ course.shortdescription },
transcript = ${ course.transcript },
project_home = ${ course.project_home },
repository = ${ course.repository },
category = ${ course.category },
image = ${ course.image },
privacy = ${ course.privacy },
language = ${ course.language },
keywords = ${ course.keywords },
goals = ${ course.goals },
instructionallevel = ${ course.instructionallevel },
audience = ${ course.audience },
studenttasks = ${ course.studenttasks },
sections = ${ course.sections },
active = ${ course.active },
video = ${ course.video },
paypal_button_id = ${ course.paypal_button_id },
prerequisite_ids = ${ course.prerequisite_ids }
where id = ${ course.id }"""
.update
.withUniqueGeneratedKeys("id")
val modifiedCourse: Course = update.transact(SQLSupport.xa).unsafeRunSync
modifiedCourse
}
有没有更好的方法?
如果您使用的是 Postgres,可以看看 Rob Norris 的另一个库 - skunk。
它允许您编写自定义编解码器:
case class City(id: Int, name: String, code: String, district: String, pop: Int)
val city: Codec[City] =
(int4 ~ varchar ~ bpchar(3) ~ varchar ~ int4).gimap[City]
val insertCity: Command[City] =
sql"""
INSERT INTO city
VALUES ($city)
""".command
Doobie 具有来自 getquill.io 的 Quill 集成,它允许您对 sql DML 的用例进行建模 类 https://github.com/polyvariant/doobie-quill
Doobie
文档非常棒,但有时您可能会发现自己遇到的某些场景并没有在他们的文档中直接解释。
为了直接插入一个 case class
对象(不是它们的属性),你必须定义一个 Write[A]
来告诉 Doobie
必须如何插入数据。当属性映射 class 与数据库 table.
想象一下下面的情况class:
case class Course (id: UUID, name: String, year: Int)
在这种情况下,我们需要为doobie定义一个Write[Course]
,即:
// Scala 3:
given Write[Course] = Write[(UUID, String, Int)].contramap(c => (c.id, c.name, c.year))
// Scala 2:
implicit val writer : Write[Course] = Write[(UUID, String, Int)].contramap(c => (c.id, c.name, c.year))
现在,您可以 运行 您的 Update
和 Doobie
将知道如何映射您的列:
def insertCourse(course: Course): Update0 =
sql"""INSERT INTO courses (id, name, year) VALUES ($course)""".update
此外,您可能需要这些导入:
import doobie.implicits.*
import doobie.implicits.javasql.*
import doobie.postgres.implicits.*
import doobie.*
如果您的 case class
属性及其类型与数据库 table 中指定的完全匹配,则无需手动指定 Writer[Course]
因为 Doobie 会自动为你推导它 [1] 这应该对你有用:
case class Course (id: UUID, name: String, year: Int)
def insertCourse(course: Course): Update0 =
sql"""INSERT INTO courses (id, name, year) VALUES ($course)""".update
Credits to my partner Y.C. that helped me to resolve this issue!
小提示:万一大家也遇到了呢
doobie.syntax.SqlInterpolator.SingleFragment[_]; incompatible interpolation method sql
以 David Corral 为例:
而不是
def insertCourse(course: Course): Update0 =
sql"""INSERT INTO courses (id, name, year) VALUES ($course)""".update
尝试在片段中包装课程变量
def insertCourse(course: Course): Update0 =
sql"""INSERT INTO courses (id, name, year) VALUES (${Fragments.values(course)})""".update