Scala Slick - 在表中插入省略一些列并返回新行的主键

问题描述

对于 Scala 项目,我使用 play-slickplay-slick-evolutions 版本 5.0.0,并使用 slick-codegen 版本 3.3.3 生成我的数据库类。

我有一个带有 primary key 列的表和一些带有 default values 的列。我想插入一行而不提及主键列或任何具有认值的列。理想情况下,此操作应返回所创建行的新主键。

我的问题是从 slick-codegen 生成代码似乎只允许插入完整的行,因为它对行使用了自己的 case 类。这是生成代码的样子(没有注释):

case class SalesOrderRow(idSalesOrder: Int,fkCustomer: Int,createdAt: java.sql.Timestamp,createdBy: Option[String] = None)

implicit def GetResultSalesOrderRow(implicit e0: GR[Int],e1: GR[java.sql.Timestamp],e2: GR[Option[String]]): GR[SalesOrderRow] = GR{
  prs => import prs._
  SalesOrderRow.tupled((<<[Int],<<[Int],<<[java.sql.Timestamp],<<?[String]))
}

class SalesOrder(_tableTag: Tag) extends profile.api.Table[SalesOrderRow](_tableTag,Some("test"),"sales_order") {
  def * = (idSalesOrder,fkCustomer,createdAt,createdBy) <> (SalesOrderRow.tupled,SalesOrderRow.unapply)
  def ? = ((Rep.some(idSalesOrder),Rep.some(fkCustomer),Rep.some(createdAt),createdBy)).shaped.<>({r=>import r._; _1.map(_=> SalesOrderRow.tupled((_1.get,_2.get,_3.get,_4)))},(_:Any) =>  throw new Exception("Inserting into ? projection not supported."))

  val idSalesOrder: Rep[Int] = column[Int]("id_sales_order",O.AutoInc,O.PrimaryKey)
  val fkCustomer: Rep[Int] = column[Int]("fk_customer")
  val createdAt: Rep[java.sql.Timestamp] = column[java.sql.Timestamp]("created_at")
  val createdBy: Rep[Option[String]] = column[Option[String]]("created_by",O.Length(20,varying=true),O.Default(None))
}

lazy val SalesOrder = new TableQuery(tag => new SalesOrder(tag))

使用这个生成代码,我现在可以插入一行并提及整列:

val insertActionsNotSoNice =
  DBIO.seq(
    salesOrders += Tables.SalesOrderRow(0,3,new Timestamp(System.currentTimeMillis()),Some("这个不好"))
  )

但我想省略开头的主键和具有认值的时间戳参数。但不能做这样的事情。

val insertActionsNotCompiling =
  DBIO.seq(
    salesOrders.map(so => (so.fkCustomer,so.createdBy) += (3,Some("这个好")))
  )

我在精美的文档和网络中发现了许多类似后一种方法的示例,但它们用于数据库的类总是有一个元组而不是自己的行类。喜欢...

class Coffees(tag: Tag) extends Table[(String,Int,Double,Int)](tag,"COFFEES")

代替

class Coffees(_tableTag: Tag) extends profile.api.Table[CoffeesRow](_tableTag,"coffee")

我是否必须将 slick-codegen 扔出我的项目并自己编写所有类以满足我的需要,还是我使用 play-slick 组合库是错误的?或者在插入未记录的内容时是否有一个简单的技巧可以省略列?

解决方法

经过几天的尝试,我找到了一个解决方案。

您可以映射查询以仅选择要插入的列,然后如果要返回插入行的 ID,则使用 returning

import dao.Tables.profile.api._

// ...

val salesOrders = TableQuery[SalesOrder]
val insertStatement = salesOrders.map(so => (so.fkCustomer,so.createdBy)) returning salesOrders.map(_.idSalesOrder) into ((_,id) => id)

然后你可以这样写:

val newSalesOrderIdFuture = db.run(insertStatement += (5,Some("有效")))

所以我在插入语句中省略了主键列 (id_sales_order) 和默认为 now() 的时间戳列 (created_at)。