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

时间:2016-08-28 05:42:07

标签: scala slick

我试图实现提供的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$12$lzycompute(JdbcActionComponent.scala:633)
    at slick.driver.JdbcActionComponent$ReturningInsertActionComposerImpl.x$12(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$2.liftedTree1$1(DatabaseComponent.scala:237)
    at slick.backend.DatabaseComponent$DatabaseDef$$anon$2.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,仍可提供此异常发生的原因信息

2 个答案:

答案 0 :(得分:1)

似乎在

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

您正在尝试返回令牌列表,而您的dbms只允许返回一个: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))

答案 1 :(得分:1)

我认为您的TokenEntity看起来像这样,其中id会自动增加并自动创建token

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

现在,您要求DBMS在插入操作中返回多个列(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))
)

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