You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Slick插入自增字段时遇赋值错误与类型不匹配问题求助

Slick自增ID插入报错的问题分析与解决

问题重现

用户定义了Scala样例类PerformanceDto和Slick表映射PerformanceTableDto,尝试实现带自增ID的插入逻辑时,遇到两个编译错误:

  1. item.copy(id=Some(id))行提示“Reassignment to val”
  2. 函数返回时提示类型不匹配:Required (Int, String, String, String, LocalDate, String, String) Found Performance

相关代码如下:

样例类与表映射

case class PerformanceDto(id: Option[Int], title: String, director: String, stage: String, premierDate: LocalDate,
                   description: String, photoReference: String)
import slick.jdbc.PostgresProfile.api._
import slick.lifted.TableQuery

class PerformanceTableDto(tag: Tag) extends Table[PerformanceDto](tag, "performances") {
  def id = column[Option[Int]]("id", O.PrimaryKey, O.AutoInc) // primary key column
  def title = column[String]("title")
  def director = column[String]("director")
  def stage = column[String]("stage")
  def premierDate = column[LocalDate]("premier_date")
  def description = column[String]("description")
  def photoReference = column[String]("photo_reference")

  def * = (id, title, director, stage, premierDate, description, photoReference) <> 
  ((PerformanceDto.apply _).tupled, PerformanceDto.unapply)
}

插入逻辑代码

private val PerformancesQuery = TableQuery[PerformanceTableDto]

def createPerformance(performance: Performance): Future[PerformanceDto] = {
  val insertQuery = PerformancesQuery returning PerformancesQuery.map(_.id) into {(item, id) => item.copy(id=Some(id))}
  val action = insertQuery += PerformanceDto(None, performance.title, performance.director, performance.stage,
      performance.premierDate, performance.description, performance.photoReference)
  db.run(action)
}

错误原因

  1. 自增列类型定义错误:数据库自增主键为非空值,表映射中id列定义为Option[Int]不符合Slick自增列处理逻辑,应该定义为Int,再通过转换适配样例类的可选字段。
  2. 映射关系不匹配:原表的*映射直接将Option[Int]列与样例类字段绑定,导致插入返回时类型不兼容,触发类型错误。
  3. into函数类型冲突:由于自增列类型错误,返回的id类型与样例类id字段的预期类型不匹配,导致item.copy操作出现编译错误。

修复方案

步骤1:修正表映射的自增列定义与映射

将id列改为Int类型,并通过.?转换为Option[Int]匹配样例类字段,同时调整*映射:

class PerformanceTableDto(tag: Tag) extends Table[PerformanceDto](tag, "performances") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc) // 修正为Int类型
  def title = column[String]("title")
  def director = column[String]("director")
  def stage = column[String]("stage")
  def premierDate = column[LocalDate]("premier_date")
  def description = column[String]("description")
  def photoReference = column[String]("photo_reference")

  // 用id.?将Int列转换为Option[Int],适配样例类的id字段
  def * = (id.?, title, director, stage, premierDate, description, photoReference) <> 
  ((PerformanceDto.apply _).tupled, PerformanceDto.unapply)
}

步骤2:修正插入逻辑的returning与into部分

此时返回的id为Int类型,直接用Some(id)赋值给样例类的id字段即可:

private val PerformancesQuery = TableQuery[PerformanceTableDto]

def createPerformance(performance: Performance): Future[PerformanceDto] = {
  // 返回自增ID,并用into将其赋值回插入的实体
  val insertQuery = PerformancesQuery returning PerformancesQuery.map(_.id) into {(item, generatedId) =>
    item.copy(id = Some(generatedId))
  }
  // 构造待插入的PerformanceDto,id传入None
  val dtoToInsert = PerformanceDto(
    id = None,
    title = performance.title,
    director = performance.director,
    stage = performance.stage,
    premierDate = performance.premierDate,
    description = performance.description,
    photoReference = performance.photoReference
  )
  val action = insertQuery += dtoToInsert
  db.run(action)
}

说明

  • 自增列在数据库中是NOT NULL约束,表映射定义为Int更符合实际,通过id.?可自动转换为Option[Int]适配样例类的可选字段。
  • returning ... into的作用是将数据库生成的自增ID赋值回插入的实体,确保返回的PerformanceDto包含完整的ID信息。

内容的提问来源于stack exchange,提问作者Jelly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 12:18:16