尝试 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。
实施是基于 的回答,这不仅仅是有见地的。
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 主键的情况下做到这一点。如果不是,那么我想这是一条单行道。
我的目标是在插入时检索 Board
实体。如果该实体存在,那么我只想 return 现有对象(与 add 方法的参数一致)。否则我想 return 将新行插入数据库。
我正在使用带有 Slick 3.2 和 MySQL 5.7 的 Play 2.7。
实施是基于
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 主键的情况下做到这一点。如果不是,那么我想这是一条单行道。