Slick插入自增字段时遇赋值错误与类型不匹配问题求助
Slick自增ID插入报错的问题分析与解决
问题重现
用户定义了Scala样例类PerformanceDto和Slick表映射PerformanceTableDto,尝试实现带自增ID的插入逻辑时,遇到两个编译错误:
item.copy(id=Some(id))行提示“Reassignment to val”- 函数返回时提示类型不匹配:
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) }
错误原因
- 自增列类型定义错误:数据库自增主键为非空值,表映射中
id列定义为Option[Int]不符合Slick自增列处理逻辑,应该定义为Int,再通过转换适配样例类的可选字段。 - 映射关系不匹配:原表的
*映射直接将Option[Int]列与样例类字段绑定,导致插入返回时类型不兼容,触发类型错误。 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
相关产品推荐
相关产品推荐

