如何在Play2.6+Slick3中更新SQL Server标识列?
解决SQL Server + Slick 3更新时的IDENTITY列错误
你遇到的Cannot update identity column 'ID'错误很典型——SQL Server的IDENTITY(自增)列是数据库自动维护的,不允许手动更新,而你的代码里在更新时给topic对象设置了id = Some(id),导致Slick生成的SQL语句试图修改这个受保护的列,直接触发了数据库的限制。
下面是具体的解决步骤:
1. 确认Slick表定义正确标记自增列
首先检查你的Slick表映射,确保ID列被标记为O.AutoInc,这样Slick就知道这是一个自增主键,不会在更新操作中默认包含它:
import slick.jdbc.SQLServerProfile.api._ class Topics(tag: Tag) extends Table[Topic](tag, "topics") { // 标记ID为自增主键 def id = column[Long]("ID", O.PrimaryKey, O.AutoInc) // 其他业务列示例 def title = column[String]("TITLE") def content = column[String]("CONTENT") // 映射到你的Topic case class,注意id是可选类型(插入时不需要提供) def * = (id.?, title, content) <> (Topic.tupled, Topic.unapply) } // 对应的Topic case class case class Topic(id: Option[Long], title: String, content: String)
2. 修改DAO的update方法,排除ID列
不要直接更新整个Topic对象,而是明确指定需要更新的业务列,完全避开ID列。比如DAO的update方法可以写成:
class TopicDao(db: Database) { private val topics = TableQuery[Topics] def update(id: Long, topic: Topic): Future[Int] = { db.run( topics.filter(_.id === id) // 只选择需要更新的列,不要包含id .map(t => (t.title, t.content)) // 传入对应的字段值 .update((topic.title, topic.content)) ) } }
3. 调整Controller中的调用逻辑
在Controller里调用update时,不需要给topic设置id,直接传入表单绑定后的topic即可:
def update(id: Long): Action[AnyContent] = Action.async { implicit request => topicForm.bindFromRequest.fold( formWithErrors => Future.successful(BadRequest(html.editForm(id, formWithErrors))), topic => { // 不要用topic.copy(id = Some(id)),直接传原topic val futureTopUpdate = dao.update(id, topic) futureTopUpdate.map { _ => Redirect(routes.TopicController.list()).flashing("success" -> "Topic updated") } } ) }
为什么这样能解决问题?
SQL Server的IDENTITY列是由数据库自动维护的,任何试图手动更新它的语句都会被拒绝。通过让Slick只更新你指定的业务列,完全不触碰ID列,就能避开这个限制。同时,Slick的O.AutoInc标记也会在生成SQL时自动忽略该列的更新操作。
内容的提问来源于stack exchange,提问作者Mani Kannan
相关产品推荐
相关产品推荐

