尝试 return 插入行时 Slick 插入不起作用

Slick insert not working while trying to return inserted row

我的目标是在插入时检索 Board 实体。如果该实体存在,那么我只想 return 现有对象(与 add 方法的参数一致)。否则我想 return 将新行插入数据库。

我正在使用带有 Slick 3.2 和 MySQL 5.7 的 Play 2.7。

实施是基于 的回答,这不仅仅是有见地的。

也来自Essential Slick

exec(messages returning messages += Message("Dave", "So... what do we do now?"))

DAO代码

@Singleton
class SlickDao @Inject()(db: Database,implicit val playDefaultContext: ExecutionContext) extends MyDao {


    override def add(board: Board): Future[Board] = {
        val insert = Boards
                .filter(b => b.id === board.id && ).exists.result.flatMap { exists =>
            if (!exists) Boards returning Boards += board
            else DBIO.successful(board) // no-op - return specified board
        }.transactionally

        db.run(insert)
    }

编辑:还尝试用

替换+=部分
Boards returning Boards.map(_.id) into { (b, boardId) => sb.copy(id = boardId) } += board

这也行不通

table定义如下:

object Board {

    val Boards: TableQuery[BoardTable] = TableQuery[BoardTable]

    class BoardTable(tag: Tag) extends Table[BoardRow](tag, "BOARDS") {

        // columns
        def id = column[String]("ID", O.Length(128))
        def x = column[String]("X")
        def y = column[Option[Int]]("Y")

        // foreign key definitions
        .....
        // primary key definitions
        def pk = primaryKey("PK_BOARDS", (id,y))


        // default projection
        def * = (boardId, x, y).mapTo[BoardRow]

    }
}

我希望 table 中会有一个新行,但是尽管 exists 查询得到执行

select exists(select `ID`, `X`, `Y`
              from `BOARDS`
              where ((`ID` = '92f10c23-2087-409a-9c4f-eb2d4d6c841f'));

结果是false没有插入。

数据库中没有任何日志记录收到任何插入语句(我指的是 general_log 文件)

所以首先,查询执行的问题是对 DAO 产生的期货处理不当。我正在将插入语句分配给未来,但这个未来从未提交给执行上下文。更糟糕的是我在问题描述中没有提到它。

但是当这个问题真正得到解决时,我可以在我的应用程序日志中看到实际的错误。堆栈跟踪如下:

slick.SlickException: This DBMS allows only a single column to be returned from an INSERT, and that column must be an AutoInc column.
   at slick.jdbc.JdbcStatementBuilderComponent$JdbcCompiledInsert.buildReturnColumns(JdbcStatementBuilderComponent.scala:67)
   at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.x$lzycompute(JdbcActionComponent.scala:659)
   at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.x(JdbcActionComponent.scala:659)
   at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.keyColumns$lzycompute(JdbcActionComponent.scala:659)
   at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.keyColumns(JdbcActionComponent.scala:659)

所以这是一个 MySQL 的核心。我不得不重新设计我的架构,以便在插入后进行检索。这种重新设计包括引入专用主键(与业务逻辑完全无关),它也是堆栈跟踪规定的 AutoInc 列。

最终解决方案变得过于复杂,而是决定使用 add 方法的实际参数来 return 如果插入实际上是成功的。所以 add 方法的实现最终是这样的

override def add(board: Board): Future[Board] = {
        db.run(Boards.insertOrUpdate(board).map(_ => board))
    }

虽然在调用底层存储库的控制器中有一些适当的 Future 错误处理。

如果您足够幸运并且没有将 MySQL 与 Slick 一起使用,我想您可以在没有专用 AutoInc 主键的情况下做到这一点。如果不是,那么我想这是一条单行道。