【问题标题】:Slick insert not working while trying to return inserted row尝试返回插入的行时,光滑的插入不起作用
【发布时间】:2019-06-05 10:35:59
【问题描述】:

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

我将 Play 2.7 与 Slick 3.2 和 MySQL 5.7 一起使用。

该实现基于this 答案,这非常有见地。

同样来自Essential Slick

exec(消息返回消息 += Message("Dave", "那么……我们现在该怎么办?"))

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

这也不起作用

表定义如下:

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]

    }
}

我希望表中会有一个新行,但是尽管exists 查询被执行了

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

结果是false 没有插入。

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

【问题讨论】:

  • 你有几个错别字,“ sb => b.id === board.id && ”,还有一个有 2 个主键的表?
  • 你是对的。我修正了拼写错误(不想完全粘贴我的代码,这些是编辑错误)并调整了表定义(通过同时添加新的演变和 SQL 约束)。不幸的是,错误仍然存​​在。没有任何插入发生。
  • 尝试改变这个 def * = (boardId, x, y) (BoardRow.tupled, BoardRow.unapply)
  • 谢谢,但这没有效果。没有帮助。

标签: scala playframework slick


【解决方案1】:

首先,查询执行的问题是对 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$17$lzycompute(JdbcActionComponent.scala:659)
   at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.x$17(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 方法的实际参数来返回如果插入实际成功。所以add方法的实现最终是这样的

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 2011-10-24
    • 1970-01-01
    • 2018-01-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多