如何修复要从 INSERT 返回的单个 AutoInc 列的 Slick Exception

How to fix Slick Exception of single AutoInc column to be returned from an INSERT

我尝试实现 akka-http rest 示例 https://github.com/ArchDev/akka-http-rest 但我坚持

    [ERROR] [08/28/2016 10:35:34.091] [default-akka.actor.default-dispatcher-8] [akka.actor.ActorSystemImpl(default)] Error during processing of request HttpRequest(HttpMethod(POST),http://127.0.0.1:9000/v1/auth/signIn,List(Host: 127.0.0.1:9000, Connection: keep-alive, Cache-Control: no-cache, Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop, User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36, Postman-Token: ec513598-b8bc-87ca-8eba-743b214fe1fa, Accept: */*, Accept-Encoding: gzip, deflate, Accept-Language: en-US, en;q=0.8, Timeout-Access: <function1>),HttpEntity.Strict(application/json,  {
    "login": "admin",
    "password": "admin"
  }),HttpProtocol(HTTP/1.1))
slick.SlickException: This DBMS allows only a single AutoInc column to be returned from an INSERT
    at slick.driver.JdbcStatementBuilderComponent$JdbcCompiledInsert.buildReturnColumns(JdbcStatementBuilderComponent.scala:69)
    at slick.driver.JdbcActionComponent$ReturningInsertActionComposerImpl.x$lzycompute(JdbcActionComponent.scala:633)
    at slick.driver.JdbcActionComponent$ReturningInsertActionComposerImpl.x(JdbcActionComponent.scala:633)
    at slick.driver.JdbcActionComponent$ReturningInsertActionComposerImpl.keyColumns$lzycompute(JdbcActionComponent.scala:633)
    at slick.driver.JdbcActionComponent$ReturningInsertActionComposerImpl.keyColumns(JdbcActionComponent.scala:633)
    at slick.driver.JdbcActionComponent$ReturningInsertActionComposerImpl.preparedInsert(JdbcActionComponent.scala:636)
    at slick.driver.JdbcActionComponent$InsertActionComposerImpl$SingleInsertAction.run(JdbcActionComponent.scala:504)
    at slick.driver.JdbcActionComponent$SimpleJdbcDriverAction.run(JdbcActionComponent.scala:32)
    at slick.driver.JdbcActionComponent$SimpleJdbcDriverAction.run(JdbcActionComponent.scala:29)
    at slick.backend.DatabaseComponent$DatabaseDef$$anon.liftedTree1(DatabaseComponent.scala:237)
    at slick.backend.DatabaseComponent$DatabaseDef$$anon.run(DatabaseComponent.scala:237)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

这是 Scala 代码: 报名 API:

path("signUp") {
    pathEndOrSingleSlash {
      post {
        entity(as[UserEntity]) { userEntity =>
          complete(Created -> signUp(userEntity).map(_.asJson))
        }
      }
    }
  }

UserEntityTable.scala

package oc.api.models.db

/**
  * Created by sujit on 8/27/16.
  */
import oc.api.models.UserEntity
import oc.api.utils.DatabaseService
trait UserEntityTable {
  protected val databaseService: DatabaseService
  import databaseService.driver.api._

  class Users(tag: Tag) extends Table[UserEntity](tag, "users") {
    def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
    def username = column[String]("username")
    def password = column[String]("password")

    def * = (id, username, password) <> ((UserEntity.apply _).tupled, UserEntity.unapply)
  }

  protected val users = TableQuery[Users]
}

UserEntity.scala

package oc.api.models

/**
  * Created by sujit on 8/27/16.
  */
case class UserEntity(id: Option[Long] = None, username: String, password: String) {
  require(!username.isEmpty, "username.empty")
  require(!password.isEmpty, "password.empty")
}

case class UserEntityUpdate(username: Option[String] = None, password: Option[String] = None) {
  def merge(user: UserEntity): UserEntity = {
    UserEntity(user.id, username.getOrElse(user.username), password.getOrElse(user.password))
  }
}

AuthService.scala

package oc.api.services

import oc.api.models.{TokenEntity, UserEntity}
import oc.api.models.db.TokenEntityTable
import oc.api.utils.DatabaseService

import scala.concurrent.{ExecutionContext, Future}

/**
  * Created by sujit on 8/27/16.
  */
class AuthService(val databaseService: DatabaseService)(usersService: UsersService)(implicit executionContext: ExecutionContext) extends TokenEntityTable {

  import databaseService._
  import databaseService.driver.api._

  def signUp(newUser: UserEntity): Future[TokenEntity] = {
    usersService.createUser(newUser).flatMap(user => createToken(user))
  }

  def authenticate(token: String): Future[Option[UserEntity]] =
    db.run((for {
      token <- tokens.filter(_.token === token)
      user <- users.filter(_.id === token.userId)
    } yield user).result.headOption)

  def createToken(user: UserEntity): Future[TokenEntity] = db.run(tokens returning tokens += TokenEntity(userId = user.id))

}

因为我是 Scala 和 Slick 的新手,无论如何我都可以提供为什么会出现此异常的信息,即使我已经在模型

中定义了 O.AutoInc

好像在

  def createToken(user: UserEntity): Future[TokenEntity] = db.run(tokens returning tokens += TokenEntity(userId = user.id))

您正在尝试 return 令牌列表,而您的 dbms 只允许 return 一个:slick.SlickException: This DBMS allows only a single AutoInc column to be returned from an INSERT

这对你有用吗?

  def createToken(user: UserEntity): Future[TokenEntity] = db.run((self returning self) += TokenEntity(userId = user.id))

我假设您的 TokenEntity 看起来像这样,其中 id 是自动递增的并且 token 是自动创建的:

case class TokenEntity(id: Option[Long] = None, userId: Long, token: String)

现在您要求 DBMS 在您的插入操作中 return 多个列(iduserIdtoken),这是不支持的。更改

def createToken(user: UserEntity): Future[TokenEntity] = db.run(tokens returning tokens += TokenEntity(userId = user.id))

def createToken(user: UserEntity): Future[TokenEntity] = db.run(
  tokens returning tokens.map(_.userId) into((token, id) => token.copy(id = Some(id)) += TokenEntity(userId = user.id))
)

此处您指定要在插入时 return 列 id。以下 into 方法合并 token 和生成的密钥。有关更详细的说明,请参阅 Inserting