Doobie更新和插入案例类语法

Mik*_*inn 7 scala doobie

Doobie可以select *使用case类来方便且正确地传递参数,但是我看不到如何使用updateand 进行类似的工作insert

例如,给定这样的案例类:

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 = ""
)
Run Code Online (Sandbox Code Playgroud)

我可以很好地select记录。之所以可以使用这种不错的语法,是因为Doobie会遍历Coursecase类的属性,并通过将其名称与courses数据库记录字段匹配来为它们分配值:

    def find(id: Id): Option[Course] =
      sql"select * from courses where id = $id"
        .query[Course]
        .option
        .transact(SQLSupport.xa)
        .unsafeRunSync
Run Code Online (Sandbox Code Playgroud)

但是,insert要求所有案例类属性都必须手动列出并与值匹配,这很可怕且容易出错:

    /** @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
    }
Run Code Online (Sandbox Code Playgroud)

同样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
    }
Run Code Online (Sandbox Code Playgroud)

有没有更好的办法?

Dav*_*ral 5

Doobie文档非常好,但有时您可能会发现自己处于某些文档中没有直接解释的场景中。

为了直接插入case class对象(而不是其属性),您必须定义一个Write[A]告诉Doobie如何插入数据的对象。当案例类中的属性映射与数据库表中的属性映射略有不同时,可以使用此方法。

想象一下以下案例类:

case class Course (id: UUID, name: String, year: Int)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们需要定义一个Write[Course]for doobie,它是:

// 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))
Run Code Online (Sandbox Code Playgroud)

现在,您可以运行您的Update并将Doobie知道如何映射您的列:

def insertCourse(course: Course): Update0 =
    sql"""INSERT INTO courses (id, name, year) VALUES ($course)""".update
Run Code Online (Sandbox Code Playgroud)

此外,您可能需要这些导入:

import doobie.implicits.*
import doobie.implicits.javasql.*
import doobie.postgres.implicits.*
import doobie.*
Run Code Online (Sandbox Code Playgroud)

如果您的case class属性及其类型与数据库表中指定的属性及其类型完全匹配,则无需手动指定,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
Run Code Online (Sandbox Code Playgroud)

感谢我的合作伙伴 YC 帮助我解决了这个问题!